b11f948c6e4ad5600c2e977aaedda95d896f68a4
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938         var im = {
2939             tag: 'input',
2940             type : 'file',
2941             cls : 'd-none  roo-card-upload-selector' 
2942           
2943         };
2944         if (this.multiple) {
2945             im.multiple = 'multiple';
2946         }
2947         
2948         return  {
2949             cls :'div' ,
2950             cn : [
2951                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2952                 im
2953
2954             ]
2955         };
2956            
2957          
2958     },
2959      
2960    
2961     initEvents : function()
2962     {
2963         
2964         Roo.bootstrap.Button.prototype.initEvents.call(this);
2965         
2966         
2967         
2968         
2969         
2970         this.urlAPI = (window.createObjectURL && window) || 
2971                                 (window.URL && URL.revokeObjectURL && URL) || 
2972                                 (window.webkitURL && webkitURL);
2973                         
2974          
2975          
2976          
2977         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2978         
2979         this.selectorEl.on('change', this.onFileSelected, this);
2980          
2981          
2982        
2983     },
2984     
2985    
2986     onClick : function(e)
2987     {
2988         e.preventDefault();
2989         
2990         if ( this.fireEvent('beforeselect', this) === false) {
2991             return;
2992         }
2993          
2994         this.selectorEl.dom.click();
2995          
2996     },
2997     
2998     onFileSelected : function(e)
2999     {
3000         e.preventDefault();
3001         
3002         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003             return;
3004         }
3005         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006         this.selectorEl.dom.value  = '';// hopefully reset..
3007         
3008         this.fireEvent('uploaded', this,  files );
3009         
3010     },
3011     
3012        
3013    
3014     
3015     /**
3016      * addCard - add an Attachment to the uploader
3017      * @param data - the data about the image to upload
3018      *
3019      * {
3020           id : 123
3021           title : "Title of file",
3022           is_uploaded : false,
3023           src : "http://.....",
3024           srcfile : { the File upload object },
3025           mimetype : file.type,
3026           preview : false,
3027           is_deleted : 0
3028           .. any other data...
3029         }
3030      *
3031      * 
3032     */
3033      
3034     reset: function()
3035     {
3036          
3037          this.selectorEl
3038     } 
3039     
3040     
3041     
3042     
3043 });
3044  /*
3045  * - LGPL
3046  *
3047  * image
3048  * 
3049  */
3050
3051
3052 /**
3053  * @class Roo.bootstrap.Img
3054  * @extends Roo.bootstrap.Component
3055  * Bootstrap Img class
3056  * @cfg {Boolean} imgResponsive false | true
3057  * @cfg {String} border rounded | circle | thumbnail
3058  * @cfg {String} src image source
3059  * @cfg {String} alt image alternative text
3060  * @cfg {String} href a tag href
3061  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062  * @cfg {String} xsUrl xs image source
3063  * @cfg {String} smUrl sm image source
3064  * @cfg {String} mdUrl md image source
3065  * @cfg {String} lgUrl lg image source
3066  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067  * 
3068  * @constructor
3069  * Create a new Input
3070  * @param {Object} config The config object
3071  */
3072
3073 Roo.bootstrap.Img = function(config){
3074     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3075     
3076     this.addEvents({
3077         // img events
3078         /**
3079          * @event click
3080          * The img click event for the img.
3081          * @param {Roo.EventObject} e
3082          */
3083         "click" : true,
3084         /**
3085          * @event load
3086          * The when any image loads
3087          * @param {Roo.EventObject} e
3088          */
3089         "load" : true
3090     });
3091 };
3092
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3094     
3095     imgResponsive: true,
3096     border: '',
3097     src: 'about:blank',
3098     href: false,
3099     target: false,
3100     xsUrl: '',
3101     smUrl: '',
3102     mdUrl: '',
3103     lgUrl: '',
3104     backgroundContain : false,
3105
3106     getAutoCreate : function()
3107     {   
3108         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109             return this.createSingleImg();
3110         }
3111         
3112         var cfg = {
3113             tag: 'div',
3114             cls: 'roo-image-responsive-group',
3115             cn: []
3116         };
3117         var _this = this;
3118         
3119         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3120             
3121             if(!_this[size + 'Url']){
3122                 return;
3123             }
3124             
3125             var img = {
3126                 tag: 'img',
3127                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128                 html: _this.html || cfg.html,
3129                 src: _this[size + 'Url']
3130             };
3131             
3132             img.cls += ' roo-image-responsive-' + size;
3133             
3134             var s = ['xs', 'sm', 'md', 'lg'];
3135             
3136             s.splice(s.indexOf(size), 1);
3137             
3138             Roo.each(s, function(ss){
3139                 img.cls += ' hidden-' + ss;
3140             });
3141             
3142             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143                 cfg.cls += ' img-' + _this.border;
3144             }
3145             
3146             if(_this.alt){
3147                 cfg.alt = _this.alt;
3148             }
3149             
3150             if(_this.href){
3151                 var a = {
3152                     tag: 'a',
3153                     href: _this.href,
3154                     cn: [
3155                         img
3156                     ]
3157                 };
3158
3159                 if(this.target){
3160                     a.target = _this.target;
3161                 }
3162             }
3163             
3164             cfg.cn.push((_this.href) ? a : img);
3165             
3166         });
3167         
3168         return cfg;
3169     },
3170     
3171     createSingleImg : function()
3172     {
3173         var cfg = {
3174             tag: 'img',
3175             cls: (this.imgResponsive) ? 'img-responsive' : '',
3176             html : null,
3177             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3178         };
3179         
3180         if (this.backgroundContain) {
3181             cfg.cls += ' background-contain';
3182         }
3183         
3184         cfg.html = this.html || cfg.html;
3185         
3186         if (this.backgroundContain) {
3187             cfg.style="background-image: url(" + this.src + ')';
3188         } else {
3189             cfg.src = this.src || cfg.src;
3190         }
3191         
3192         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193             cfg.cls += ' img-' + this.border;
3194         }
3195         
3196         if(this.alt){
3197             cfg.alt = this.alt;
3198         }
3199         
3200         if(this.href){
3201             var a = {
3202                 tag: 'a',
3203                 href: this.href,
3204                 cn: [
3205                     cfg
3206                 ]
3207             };
3208             
3209             if(this.target){
3210                 a.target = this.target;
3211             }
3212             
3213         }
3214         
3215         return (this.href) ? a : cfg;
3216     },
3217     
3218     initEvents: function() 
3219     {
3220         if(!this.href){
3221             this.el.on('click', this.onClick, this);
3222         }
3223         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224             this.el.on('load', this.onImageLoad, this);
3225         } else {
3226             // not sure if this works.. not tested
3227             this.el.select('img', true).on('load', this.onImageLoad, this);
3228         }
3229         
3230     },
3231     
3232     onClick : function(e)
3233     {
3234         Roo.log('img onclick');
3235         this.fireEvent('click', this, e);
3236     },
3237     onImageLoad: function(e)
3238     {
3239         Roo.log('img load');
3240         this.fireEvent('load', this, e);
3241     },
3242     
3243     /**
3244      * Sets the url of the image - used to update it
3245      * @param {String} url the url of the image
3246      */
3247     
3248     setSrc : function(url)
3249     {
3250         this.src =  url;
3251         
3252         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253             if (this.backgroundContain) {
3254                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3255             } else {
3256                 this.el.dom.src =  url;
3257             }
3258             return;
3259         }
3260         
3261         this.el.select('img', true).first().dom.src =  url;
3262     }
3263     
3264     
3265    
3266 });
3267
3268  /*
3269  * - LGPL
3270  *
3271  * image
3272  * 
3273  */
3274
3275
3276 /**
3277  * @class Roo.bootstrap.Link
3278  * @extends Roo.bootstrap.Component
3279  * @children Roo.bootstrap.Component
3280  * Bootstrap Link Class (eg. '<a href>')
3281  
3282  * @cfg {String} alt image alternative text
3283  * @cfg {String} href a tag href
3284  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285  * @cfg {String} html the content of the link.
3286  * @cfg {String} anchor name for the anchor link
3287  * @cfg {String} fa - favicon
3288
3289  * @cfg {Boolean} preventDefault (true | false) default false
3290
3291  * 
3292  * @constructor
3293  * Create a new Input
3294  * @param {Object} config The config object
3295  */
3296
3297 Roo.bootstrap.Link = function(config){
3298     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3299     
3300     this.addEvents({
3301         // img events
3302         /**
3303          * @event click
3304          * The img click event for the img.
3305          * @param {Roo.EventObject} e
3306          */
3307         "click" : true
3308     });
3309 };
3310
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3312     
3313     href: false,
3314     target: false,
3315     preventDefault: false,
3316     anchor : false,
3317     alt : false,
3318     fa: false,
3319
3320
3321     getAutoCreate : function()
3322     {
3323         var html = this.html || '';
3324         
3325         if (this.fa !== false) {
3326             html = '<i class="fa fa-' + this.fa + '"></i>';
3327         }
3328         var cfg = {
3329             tag: 'a'
3330         };
3331         // anchor's do not require html/href...
3332         if (this.anchor === false) {
3333             cfg.html = html;
3334             cfg.href = this.href || '#';
3335         } else {
3336             cfg.name = this.anchor;
3337             if (this.html !== false || this.fa !== false) {
3338                 cfg.html = html;
3339             }
3340             if (this.href !== false) {
3341                 cfg.href = this.href;
3342             }
3343         }
3344         
3345         if(this.alt !== false){
3346             cfg.alt = this.alt;
3347         }
3348         
3349         
3350         if(this.target !== false) {
3351             cfg.target = this.target;
3352         }
3353         
3354         return cfg;
3355     },
3356     
3357     initEvents: function() {
3358         
3359         if(!this.href || this.preventDefault){
3360             this.el.on('click', this.onClick, this);
3361         }
3362     },
3363     
3364     onClick : function(e)
3365     {
3366         if(this.preventDefault){
3367             e.preventDefault();
3368         }
3369         //Roo.log('img onclick');
3370         this.fireEvent('click', this, e);
3371     }
3372    
3373 });
3374
3375  /*
3376  * - LGPL
3377  *
3378  * header
3379  * 
3380  */
3381
3382 /**
3383  * @class Roo.bootstrap.Header
3384  * @extends Roo.bootstrap.Component
3385  * @children Roo.bootstrap.Component
3386  * Bootstrap Header class
3387  *
3388  * 
3389  * @cfg {String} html content of header
3390  * @cfg {Number} level (1|2|3|4|5|6) default 1
3391  * 
3392  * @constructor
3393  * Create a new Header
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.Header  = function(config){
3399     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3403     
3404     //href : false,
3405     html : false,
3406     level : 1,
3407     
3408     
3409     
3410     getAutoCreate : function(){
3411         
3412         
3413         
3414         var cfg = {
3415             tag: 'h' + (1 *this.level),
3416             html: this.html || ''
3417         } ;
3418         
3419         return cfg;
3420     }
3421    
3422 });
3423
3424  
3425
3426  /**
3427  * @class Roo.bootstrap.MenuMgr
3428  * @licence LGPL
3429  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430  * @static
3431  */
3432 Roo.bootstrap.menu.Manager = function(){
3433    var menus, active, groups = {}, attached = false, lastShow = new Date();
3434
3435    // private - called when first menu is created
3436    function init(){
3437        menus = {};
3438        active = new Roo.util.MixedCollection();
3439        Roo.get(document).addKeyListener(27, function(){
3440            if(active.length > 0){
3441                hideAll();
3442            }
3443        });
3444    }
3445
3446    // private
3447    function hideAll(){
3448        if(active && active.length > 0){
3449            var c = active.clone();
3450            c.each(function(m){
3451                m.hide();
3452            });
3453        }
3454    }
3455
3456    // private
3457    function onHide(m){
3458        active.remove(m);
3459        if(active.length < 1){
3460            Roo.get(document).un("mouseup", onMouseDown);
3461             
3462            attached = false;
3463        }
3464    }
3465
3466    // private
3467    function onShow(m){
3468        var last = active.last();
3469        lastShow = new Date();
3470        active.add(m);
3471        if(!attached){
3472           Roo.get(document).on("mouseup", onMouseDown);
3473            
3474            attached = true;
3475        }
3476        if(m.parentMenu){
3477           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478           m.parentMenu.activeChild = m;
3479        }else if(last && last.isVisible()){
3480           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3481        }
3482    }
3483
3484    // private
3485    function onBeforeHide(m){
3486        if(m.activeChild){
3487            m.activeChild.hide();
3488        }
3489        if(m.autoHideTimer){
3490            clearTimeout(m.autoHideTimer);
3491            delete m.autoHideTimer;
3492        }
3493    }
3494
3495    // private
3496    function onBeforeShow(m){
3497        var pm = m.parentMenu;
3498        if(!pm && !m.allowOtherMenus){
3499            hideAll();
3500        }else if(pm && pm.activeChild && active != m){
3501            pm.activeChild.hide();
3502        }
3503    }
3504
3505    // private this should really trigger on mouseup..
3506    function onMouseDown(e){
3507         Roo.log("on Mouse Up");
3508         
3509         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510             Roo.log("MenuManager hideAll");
3511             hideAll();
3512             e.stopEvent();
3513         }
3514         
3515         
3516    }
3517
3518    // private
3519    function onBeforeCheck(mi, state){
3520        if(state){
3521            var g = groups[mi.group];
3522            for(var i = 0, l = g.length; i < l; i++){
3523                if(g[i] != mi){
3524                    g[i].setChecked(false);
3525                }
3526            }
3527        }
3528    }
3529
3530    return {
3531
3532        /**
3533         * Hides all menus that are currently visible
3534         */
3535        hideAll : function(){
3536             hideAll();  
3537        },
3538
3539        // private
3540        register : function(menu){
3541            if(!menus){
3542                init();
3543            }
3544            menus[menu.id] = menu;
3545            menu.on("beforehide", onBeforeHide);
3546            menu.on("hide", onHide);
3547            menu.on("beforeshow", onBeforeShow);
3548            menu.on("show", onShow);
3549            var g = menu.group;
3550            if(g && menu.events["checkchange"]){
3551                if(!groups[g]){
3552                    groups[g] = [];
3553                }
3554                groups[g].push(menu);
3555                menu.on("checkchange", onCheck);
3556            }
3557        },
3558
3559         /**
3560          * Returns a {@link Roo.menu.Menu} object
3561          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562          * be used to generate and return a new Menu instance.
3563          */
3564        get : function(menu){
3565            if(typeof menu == "string"){ // menu id
3566                return menus[menu];
3567            }else if(menu.events){  // menu instance
3568                return menu;
3569            }
3570            /*else if(typeof menu.length == 'number'){ // array of menu items?
3571                return new Roo.bootstrap.Menu({items:menu});
3572            }else{ // otherwise, must be a config
3573                return new Roo.bootstrap.Menu(menu);
3574            }
3575            */
3576            return false;
3577        },
3578
3579        // private
3580        unregister : function(menu){
3581            delete menus[menu.id];
3582            menu.un("beforehide", onBeforeHide);
3583            menu.un("hide", onHide);
3584            menu.un("beforeshow", onBeforeShow);
3585            menu.un("show", onShow);
3586            var g = menu.group;
3587            if(g && menu.events["checkchange"]){
3588                groups[g].remove(menu);
3589                menu.un("checkchange", onCheck);
3590            }
3591        },
3592
3593        // private
3594        registerCheckable : function(menuItem){
3595            var g = menuItem.group;
3596            if(g){
3597                if(!groups[g]){
3598                    groups[g] = [];
3599                }
3600                groups[g].push(menuItem);
3601                menuItem.on("beforecheckchange", onBeforeCheck);
3602            }
3603        },
3604
3605        // private
3606        unregisterCheckable : function(menuItem){
3607            var g = menuItem.group;
3608            if(g){
3609                groups[g].remove(menuItem);
3610                menuItem.un("beforecheckchange", onBeforeCheck);
3611            }
3612        }
3613    };
3614 }(); 
3615 /**
3616  * @class Roo.bootstrap.menu.Menu
3617  * @extends Roo.bootstrap.Component
3618  * @licence LGPL
3619  * @children Roo.bootstrap.menu.Item
3620  * @parent none
3621  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3622  * 
3623  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3625  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3626  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3627   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3628   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3629  
3630  * @constructor
3631  * Create a new Menu
3632  * @param {Object} config The config objectQ
3633  */
3634
3635
3636 Roo.bootstrap.menu.Menu = function(config){
3637     
3638     if (config.type == 'treeview') {
3639         // normally menu's are drawn attached to the document to handle layering etc..
3640         // however treeview (used by the docs menu is drawn into the parent element)
3641         this.container_method = 'getChildContainer'; 
3642     }
3643     
3644     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645     if (this.registerMenu && this.type != 'treeview')  {
3646         Roo.bootstrap.menu.Manager.register(this);
3647     }
3648     
3649     
3650     this.addEvents({
3651         /**
3652          * @event beforeshow
3653          * Fires before this menu is displayed (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforeshow : true,
3657         /**
3658          * @event beforehide
3659          * Fires before this menu is hidden (return false to block)
3660          * @param {Roo.menu.Menu} this
3661          */
3662         beforehide : true,
3663         /**
3664          * @event show
3665          * Fires after this menu is displayed
3666          * @param {Roo.menu.Menu} this
3667          */
3668         show : true,
3669         /**
3670          * @event hide
3671          * Fires after this menu is hidden
3672          * @param {Roo.menu.Menu} this
3673          */
3674         hide : true,
3675         /**
3676          * @event click
3677          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680          * @param {Roo.EventObject} e
3681          */
3682         click : true,
3683         /**
3684          * @event mouseover
3685          * Fires when the mouse is hovering over this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseover : true,
3691         /**
3692          * @event mouseout
3693          * Fires when the mouse exits this menu
3694          * @param {Roo.menu.Menu} this
3695          * @param {Roo.EventObject} e
3696          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3697          */
3698         mouseout : true,
3699         /**
3700          * @event itemclick
3701          * Fires when a menu item contained in this menu is clicked
3702          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703          * @param {Roo.EventObject} e
3704          */
3705         itemclick: true
3706     });
3707     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 };
3709
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3711     
3712    /// html : false,
3713    
3714     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3715     type: false,
3716     /**
3717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718      */
3719     registerMenu : true,
3720     
3721     menuItems :false, // stores the menu items..
3722     
3723     hidden:true,
3724         
3725     parentMenu : false,
3726     
3727     stopEvent : true,
3728     
3729     isLink : false,
3730     
3731     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732     
3733     hideTrigger : false,
3734     
3735     align : 'tl-bl?',
3736     
3737     
3738     getChildContainer : function() {
3739         return this.el;  
3740     },
3741     
3742     getAutoCreate : function(){
3743          
3744         //if (['right'].indexOf(this.align)!==-1) {
3745         //    cfg.cn[1].cls += ' pull-right'
3746         //}
3747          
3748         var cfg = {
3749             tag : 'ul',
3750             cls : 'dropdown-menu shadow' ,
3751             style : 'z-index:1000'
3752             
3753         };
3754         
3755         if (this.type === 'submenu') {
3756             cfg.cls = 'submenu active';
3757         }
3758         if (this.type === 'treeview') {
3759             cfg.cls = 'treeview-menu';
3760         }
3761         
3762         return cfg;
3763     },
3764     initEvents : function() {
3765         
3766        // Roo.log("ADD event");
3767        // Roo.log(this.triggerEl.dom);
3768         if (this.triggerEl) {
3769             
3770             this.triggerEl.on('click', this.onTriggerClick, this);
3771             
3772             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773             
3774             if (!this.hideTrigger) {
3775                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776                     // dropdown toggle on the 'a' in BS4?
3777                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778                 } else {
3779                     this.triggerEl.addClass('dropdown-toggle');
3780                 }
3781             }
3782         }
3783         
3784         if (Roo.isTouch) {
3785             this.el.on('touchstart'  , this.onTouch, this);
3786         }
3787         this.el.on('click' , this.onClick, this);
3788
3789         this.el.on("mouseover", this.onMouseOver, this);
3790         this.el.on("mouseout", this.onMouseOut, this);
3791         
3792     },
3793     
3794     findTargetItem : function(e)
3795     {
3796         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3797         if(!t){
3798             return false;
3799         }
3800         //Roo.log(t);         Roo.log(t.id);
3801         if(t && t.id){
3802             //Roo.log(this.menuitems);
3803             return this.menuitems.get(t.id);
3804             
3805             //return this.items.get(t.menuItemId);
3806         }
3807         
3808         return false;
3809     },
3810     
3811     onTouch : function(e) 
3812     {
3813         Roo.log("menu.onTouch");
3814         //e.stopEvent(); this make the user popdown broken
3815         this.onClick(e);
3816     },
3817     
3818     onClick : function(e)
3819     {
3820         Roo.log("menu.onClick");
3821         
3822         var t = this.findTargetItem(e);
3823         if(!t || t.isContainer){
3824             return;
3825         }
3826         Roo.log(e);
3827         /*
3828         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3829             if(t == this.activeItem && t.shouldDeactivate(e)){
3830                 this.activeItem.deactivate();
3831                 delete this.activeItem;
3832                 return;
3833             }
3834             if(t.canActivate){
3835                 this.setActiveItem(t, true);
3836             }
3837             return;
3838             
3839             
3840         }
3841         */
3842        
3843         Roo.log('pass click event');
3844         
3845         t.onClick(e);
3846         
3847         this.fireEvent("click", this, t, e);
3848         
3849         var _this = this;
3850         
3851         if(!t.href.length || t.href == '#'){
3852             (function() { _this.hide(); }).defer(100);
3853         }
3854         
3855     },
3856     
3857     onMouseOver : function(e){
3858         var t  = this.findTargetItem(e);
3859         //Roo.log(t);
3860         //if(t){
3861         //    if(t.canActivate && !t.disabled){
3862         //        this.setActiveItem(t, true);
3863         //    }
3864         //}
3865         
3866         this.fireEvent("mouseover", this, e, t);
3867     },
3868     isVisible : function(){
3869         return !this.hidden;
3870     },
3871     onMouseOut : function(e){
3872         var t  = this.findTargetItem(e);
3873         
3874         //if(t ){
3875         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3876         //        this.activeItem.deactivate();
3877         //        delete this.activeItem;
3878         //    }
3879         //}
3880         this.fireEvent("mouseout", this, e, t);
3881     },
3882     
3883     
3884     /**
3885      * Displays this menu relative to another element
3886      * @param {String/HTMLElement/Roo.Element} element The element to align to
3887      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888      * the element (defaults to this.defaultAlign)
3889      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890      */
3891     show : function(el, pos, parentMenu)
3892     {
3893         if (false === this.fireEvent("beforeshow", this)) {
3894             Roo.log("show canceled");
3895             return;
3896         }
3897         this.parentMenu = parentMenu;
3898         if(!this.el){
3899             this.render();
3900         }
3901         this.el.addClass('show'); // show otherwise we do not know how big we are..
3902          
3903         var xy = this.el.getAlignToXY(el, pos);
3904         
3905         // bl-tl << left align  below
3906         // tl-bl << left align 
3907         
3908         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909             // if it goes to far to the right.. -> align left.
3910             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911         }
3912         if(xy[0] < 0){
3913             // was left align - go right?
3914             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915         }
3916         
3917         // goes down the bottom
3918         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919            xy[1]  < 0 ){
3920             var a = this.align.replace('?', '').split('-');
3921             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3922             
3923         }
3924         
3925         this.showAt(  xy , parentMenu, false);
3926     },
3927      /**
3928      * Displays this menu at a specific xy position
3929      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931      */
3932     showAt : function(xy, parentMenu, /* private: */_e){
3933         this.parentMenu = parentMenu;
3934         if(!this.el){
3935             this.render();
3936         }
3937         if(_e !== false){
3938             this.fireEvent("beforeshow", this);
3939             //xy = this.el.adjustForConstraints(xy);
3940         }
3941         
3942         //this.el.show();
3943         this.hideMenuItems();
3944         this.hidden = false;
3945         if (this.triggerEl) {
3946             this.triggerEl.addClass('open');
3947         }
3948         
3949         this.el.addClass('show');
3950         
3951         
3952         
3953         // reassign x when hitting right
3954         
3955         // reassign y when hitting bottom
3956         
3957         // but the list may align on trigger left or trigger top... should it be a properity?
3958         
3959         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3960             this.el.setXY(xy);
3961         }
3962         
3963         this.focus();
3964         this.fireEvent("show", this);
3965     },
3966     
3967     focus : function(){
3968         return;
3969         if(!this.hidden){
3970             this.doFocus.defer(50, this);
3971         }
3972     },
3973
3974     doFocus : function(){
3975         if(!this.hidden){
3976             this.focusEl.focus();
3977         }
3978     },
3979
3980     /**
3981      * Hides this menu and optionally all parent menus
3982      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983      */
3984     hide : function(deep)
3985     {
3986         if (false === this.fireEvent("beforehide", this)) {
3987             Roo.log("hide canceled");
3988             return;
3989         }
3990         this.hideMenuItems();
3991         if(this.el && this.isVisible()){
3992            
3993             if(this.activeItem){
3994                 this.activeItem.deactivate();
3995                 this.activeItem = null;
3996             }
3997             if (this.triggerEl) {
3998                 this.triggerEl.removeClass('open');
3999             }
4000             
4001             this.el.removeClass('show');
4002             this.hidden = true;
4003             this.fireEvent("hide", this);
4004         }
4005         if(deep === true && this.parentMenu){
4006             this.parentMenu.hide(true);
4007         }
4008     },
4009     
4010     onTriggerClick : function(e)
4011     {
4012         Roo.log('trigger click');
4013         
4014         var target = e.getTarget();
4015         
4016         Roo.log(target.nodeName.toLowerCase());
4017         
4018         if(target.nodeName.toLowerCase() === 'i'){
4019             e.preventDefault();
4020         }
4021         
4022     },
4023     
4024     onTriggerPress  : function(e)
4025     {
4026         Roo.log('trigger press');
4027         //Roo.log(e.getTarget());
4028        // Roo.log(this.triggerEl.dom);
4029        
4030         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031         var pel = Roo.get(e.getTarget());
4032         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033             Roo.log('is treeview or dropdown?');
4034             return;
4035         }
4036         
4037         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038             return;
4039         }
4040         
4041         if (this.isVisible()) {
4042             Roo.log('hide');
4043             this.hide();
4044         } else {
4045             Roo.log('show');
4046             
4047             this.show(this.triggerEl, this.align, false);
4048         }
4049         
4050         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4051             e.stopEvent();
4052         }
4053         
4054     },
4055        
4056     
4057     hideMenuItems : function()
4058     {
4059         Roo.log("hide Menu Items");
4060         if (!this.el) { 
4061             return;
4062         }
4063         
4064         this.el.select('.open',true).each(function(aa) {
4065             
4066             aa.removeClass('open');
4067          
4068         });
4069     },
4070     addxtypeChild : function (tree, cntr) {
4071         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072           
4073         this.menuitems.add(comp);
4074         return comp;
4075
4076     },
4077     getEl : function()
4078     {
4079         Roo.log(this.el);
4080         return this.el;
4081     },
4082     
4083     clear : function()
4084     {
4085         this.getEl().dom.innerHTML = '';
4086         this.menuitems.clear();
4087     }
4088 });
4089
4090  
4091  /**
4092  * @class Roo.bootstrap.menu.Item
4093  * @extends Roo.bootstrap.Component
4094  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095  * @parent Roo.bootstrap.menu.Menu
4096  * @licence LGPL
4097  * Bootstrap MenuItem class
4098  * 
4099  * @cfg {String} html the menu label
4100  * @cfg {String} href the link
4101  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4104  * @cfg {String} fa favicon to show on left of menu item.
4105  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4106  * 
4107  * 
4108  * @constructor
4109  * Create a new MenuItem
4110  * @param {Object} config The config object
4111  */
4112
4113
4114 Roo.bootstrap.menu.Item = function(config){
4115     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4116     this.addEvents({
4117         // raw events
4118         /**
4119          * @event click
4120          * The raw click event for the entire grid.
4121          * @param {Roo.bootstrap.menu.Item} this
4122          * @param {Roo.EventObject} e
4123          */
4124         "click" : true
4125     });
4126 };
4127
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4129     
4130     href : false,
4131     html : false,
4132     preventDefault: false,
4133     isContainer : false,
4134     active : false,
4135     fa: false,
4136     
4137     getAutoCreate : function(){
4138         
4139         if(this.isContainer){
4140             return {
4141                 tag: 'li',
4142                 cls: 'dropdown-menu-item '
4143             };
4144         }
4145         var ctag = {
4146             tag: 'span',
4147             html: 'Link'
4148         };
4149         
4150         var anc = {
4151             tag : 'a',
4152             cls : 'dropdown-item',
4153             href : '#',
4154             cn : [  ]
4155         };
4156         
4157         if (this.fa !== false) {
4158             anc.cn.push({
4159                 tag : 'i',
4160                 cls : 'fa fa-' + this.fa
4161             });
4162         }
4163         
4164         anc.cn.push(ctag);
4165         
4166         
4167         var cfg= {
4168             tag: 'li',
4169             cls: 'dropdown-menu-item',
4170             cn: [ anc ]
4171         };
4172         if (this.parent().type == 'treeview') {
4173             cfg.cls = 'treeview-menu';
4174         }
4175         if (this.active) {
4176             cfg.cls += ' active';
4177         }
4178         
4179         
4180         
4181         anc.href = this.href || cfg.cn[0].href ;
4182         ctag.html = this.html || cfg.cn[0].html ;
4183         return cfg;
4184     },
4185     
4186     initEvents: function()
4187     {
4188         if (this.parent().type == 'treeview') {
4189             this.el.select('a').on('click', this.onClick, this);
4190         }
4191         
4192         if (this.menu) {
4193             this.menu.parentType = this.xtype;
4194             this.menu.triggerEl = this.el;
4195             this.menu = this.addxtype(Roo.apply({}, this.menu));
4196         }
4197         
4198     },
4199     onClick : function(e)
4200     {
4201         Roo.log('item on click ');
4202         
4203         if(this.preventDefault){
4204             e.preventDefault();
4205         }
4206         //this.parent().hideMenuItems();
4207         
4208         this.fireEvent('click', this, e);
4209     },
4210     getEl : function()
4211     {
4212         return this.el;
4213     } 
4214 });
4215
4216  
4217
4218  
4219
4220   
4221 /**
4222  * @class Roo.bootstrap.menu.Separator
4223  * @extends Roo.bootstrap.Component
4224  * @licence LGPL
4225  * @parent Roo.bootstrap.menu.Menu
4226  * Bootstrap Separator class
4227  * 
4228  * @constructor
4229  * Create a new Separator
4230  * @param {Object} config The config object
4231  */
4232
4233
4234 Roo.bootstrap.menu.Separator = function(config){
4235     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 };
4237
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4239     
4240     getAutoCreate : function(){
4241         var cfg = {
4242             tag : 'li',
4243             cls: 'dropdown-divider divider'
4244         };
4245         
4246         return cfg;
4247     }
4248    
4249 });
4250
4251  
4252
4253  
4254 /*
4255 * Licence: LGPL
4256 */
4257
4258 /**
4259  * @class Roo.bootstrap.Modal
4260  * @extends Roo.bootstrap.Component
4261  * @parent none builder
4262  * @children Roo.bootstrap.Component
4263  * Bootstrap Modal class
4264  * @cfg {String} title Title of dialog
4265  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4267  * @cfg {Boolean} specificTitle default false
4268  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270  * @cfg {Boolean} animate default true
4271  * @cfg {Boolean} allow_close default true
4272  * @cfg {Boolean} fitwindow default false
4273  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276  * @cfg {String} size (sm|lg|xl) default empty
4277  * @cfg {Number} max_width set the max width of modal
4278  * @cfg {Boolean} editableTitle can the title be edited
4279
4280  *
4281  *
4282  * @constructor
4283  * Create a new Modal Dialog
4284  * @param {Object} config The config object
4285  */
4286
4287 Roo.bootstrap.Modal = function(config){
4288     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4289     this.addEvents({
4290         // raw events
4291         /**
4292          * @event btnclick
4293          * The raw btnclick event for the button
4294          * @param {Roo.EventObject} e
4295          */
4296         "btnclick" : true,
4297         /**
4298          * @event resize
4299          * Fire when dialog resize
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} e
4302          */
4303         "resize" : true,
4304         /**
4305          * @event titlechanged
4306          * Fire when the editable title has been changed
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} value
4309          */
4310         "titlechanged" : true 
4311         
4312     });
4313     this.buttons = this.buttons || [];
4314
4315     if (this.tmpl) {
4316         this.tmpl = Roo.factory(this.tmpl);
4317     }
4318
4319 };
4320
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4322
4323     title : 'test dialog',
4324
4325     buttons : false,
4326
4327     // set on load...
4328
4329     html: false,
4330
4331     tmp: false,
4332
4333     specificTitle: false,
4334
4335     buttonPosition: 'right',
4336
4337     allow_close : true,
4338
4339     animate : true,
4340
4341     fitwindow: false,
4342     
4343      // private
4344     dialogEl: false,
4345     bodyEl:  false,
4346     footerEl:  false,
4347     titleEl:  false,
4348     closeEl:  false,
4349
4350     size: '',
4351     
4352     max_width: 0,
4353     
4354     max_height: 0,
4355     
4356     fit_content: false,
4357     editableTitle  : false,
4358
4359     onRender : function(ct, position)
4360     {
4361         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362
4363         if(!this.el){
4364             var cfg = Roo.apply({},  this.getAutoCreate());
4365             cfg.id = Roo.id();
4366             //if(!cfg.name){
4367             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368             //}
4369             //if (!cfg.name.length) {
4370             //    delete cfg.name;
4371            // }
4372             if (this.cls) {
4373                 cfg.cls += ' ' + this.cls;
4374             }
4375             if (this.style) {
4376                 cfg.style = this.style;
4377             }
4378             this.el = Roo.get(document.body).createChild(cfg, position);
4379         }
4380         //var type = this.el.dom.type;
4381
4382
4383         if(this.tabIndex !== undefined){
4384             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385         }
4386
4387         this.dialogEl = this.el.select('.modal-dialog',true).first();
4388         this.bodyEl = this.el.select('.modal-body',true).first();
4389         this.closeEl = this.el.select('.modal-header .close', true).first();
4390         this.headerEl = this.el.select('.modal-header',true).first();
4391         this.titleEl = this.el.select('.modal-title',true).first();
4392         this.footerEl = this.el.select('.modal-footer',true).first();
4393
4394         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395         
4396         //this.el.addClass("x-dlg-modal");
4397
4398         if (this.buttons.length) {
4399             Roo.each(this.buttons, function(bb) {
4400                 var b = Roo.apply({}, bb);
4401                 b.xns = b.xns || Roo.bootstrap;
4402                 b.xtype = b.xtype || 'Button';
4403                 if (typeof(b.listeners) == 'undefined') {
4404                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4405                 }
4406
4407                 var btn = Roo.factory(b);
4408
4409                 btn.render(this.getButtonContainer());
4410
4411             },this);
4412         }
4413         // render the children.
4414         var nitems = [];
4415
4416         if(typeof(this.items) != 'undefined'){
4417             var items = this.items;
4418             delete this.items;
4419
4420             for(var i =0;i < items.length;i++) {
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628     },
4629
4630     show : function() {
4631
4632         if (!this.rendered) {
4633             this.render();
4634         }
4635         this.toggleHeaderInput(false);
4636         //this.el.setStyle('display', 'block');
4637         this.el.removeClass('hideing');
4638         this.el.dom.style.display='block';
4639         
4640         Roo.get(document.body).addClass('modal-open');
4641  
4642         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4643             
4644             (function(){
4645                 this.el.addClass('show');
4646                 this.el.addClass('in');
4647             }).defer(50, this);
4648         }else{
4649             this.el.addClass('show');
4650             this.el.addClass('in');
4651         }
4652
4653         // not sure how we can show data in here..
4654         //if (this.tmpl) {
4655         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4656         //}
4657
4658         Roo.get(document.body).addClass("x-body-masked");
4659         
4660         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4661         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662         this.maskEl.dom.style.display = 'block';
4663         this.maskEl.addClass('show');
4664         
4665         
4666         this.resize();
4667         
4668         this.fireEvent('show', this);
4669
4670         // set zindex here - otherwise it appears to be ignored...
4671         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672
4673         (function () {
4674             this.items.forEach( function(e) {
4675                 e.layout ? e.layout() : false;
4676
4677             });
4678         }).defer(100,this);
4679
4680     },
4681     hide : function()
4682     {
4683         if(this.fireEvent("beforehide", this) !== false){
4684             
4685             this.maskEl.removeClass('show');
4686             
4687             this.maskEl.dom.style.display = '';
4688             Roo.get(document.body).removeClass("x-body-masked");
4689             this.el.removeClass('in');
4690             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4691
4692             if(this.animate){ // why
4693                 this.el.addClass('hideing');
4694                 this.el.removeClass('show');
4695                 (function(){
4696                     if (!this.el.hasClass('hideing')) {
4697                         return; // it's been shown again...
4698                     }
4699                     
4700                     this.el.dom.style.display='';
4701
4702                     Roo.get(document.body).removeClass('modal-open');
4703                     this.el.removeClass('hideing');
4704                 }).defer(150,this);
4705                 
4706             }else{
4707                 this.el.removeClass('show');
4708                 this.el.dom.style.display='';
4709                 Roo.get(document.body).removeClass('modal-open');
4710
4711             }
4712             this.fireEvent('hide', this);
4713         }
4714     },
4715     isVisible : function()
4716     {
4717         
4718         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4719         
4720     },
4721
4722     addButton : function(str, cb)
4723     {
4724
4725
4726         var b = Roo.apply({}, { html : str } );
4727         b.xns = b.xns || Roo.bootstrap;
4728         b.xtype = b.xtype || 'Button';
4729         if (typeof(b.listeners) == 'undefined') {
4730             b.listeners = { click : cb.createDelegate(this)  };
4731         }
4732
4733         var btn = Roo.factory(b);
4734
4735         btn.render(this.getButtonContainer());
4736
4737         return btn;
4738
4739     },
4740
4741     setDefaultButton : function(btn)
4742     {
4743         //this.el.select('.modal-footer').()
4744     },
4745
4746     resizeTo: function(w,h)
4747     {
4748         this.dialogEl.setWidth(w);
4749         
4750         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4751
4752         this.bodyEl.setHeight(h - diff);
4753         
4754         this.fireEvent('resize', this);
4755     },
4756     
4757     setContentSize  : function(w, h)
4758     {
4759
4760     },
4761     onButtonClick: function(btn,e)
4762     {
4763         //Roo.log([a,b,c]);
4764         this.fireEvent('btnclick', btn.name, e);
4765     },
4766      /**
4767      * Set the title of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setTitle: function(str) {
4771         this.titleEl.dom.innerHTML = str;
4772         this.title = str;
4773     },
4774     /**
4775      * Set the body of the Dialog
4776      * @param {String} str new Title
4777      */
4778     setBody: function(str) {
4779         this.bodyEl.dom.innerHTML = str;
4780     },
4781     /**
4782      * Set the body of the Dialog using the template
4783      * @param {Obj} data - apply this data to the template and replace the body contents.
4784      */
4785     applyBody: function(obj)
4786     {
4787         if (!this.tmpl) {
4788             Roo.log("Error - using apply Body without a template");
4789             //code
4790         }
4791         this.tmpl.overwrite(this.bodyEl, obj);
4792     },
4793     
4794     getChildHeight : function(child_nodes)
4795     {
4796         if(
4797             !child_nodes ||
4798             child_nodes.length == 0
4799         ) {
4800             return 0;
4801         }
4802         
4803         var child_height = 0;
4804         
4805         for(var i = 0; i < child_nodes.length; i++) {
4806             
4807             /*
4808             * for modal with tabs...
4809             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4810                 
4811                 var layout_childs = child_nodes[i].childNodes;
4812                 
4813                 for(var j = 0; j < layout_childs.length; j++) {
4814                     
4815                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4816                         
4817                         var layout_body_childs = layout_childs[j].childNodes;
4818                         
4819                         for(var k = 0; k < layout_body_childs.length; k++) {
4820                             
4821                             if(layout_body_childs[k].classList.contains('navbar')) {
4822                                 child_height += layout_body_childs[k].offsetHeight;
4823                                 continue;
4824                             }
4825                             
4826                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4827                                 
4828                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4829                                 
4830                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4831                                     
4832                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4834                                         continue;
4835                                     }
4836                                     
4837                                 }
4838                                 
4839                             }
4840                             
4841                         }
4842                     }
4843                 }
4844                 continue;
4845             }
4846             */
4847             
4848             child_height += child_nodes[i].offsetHeight;
4849             // Roo.log(child_nodes[i].offsetHeight);
4850         }
4851         
4852         return child_height;
4853     },
4854     toggleHeaderInput : function(is_edit)
4855     {
4856         if (!this.editableTitle) {
4857             return; // not editable.
4858         }
4859         if (is_edit && this.is_header_editing) {
4860             return; // already editing..
4861         }
4862         if (is_edit) {
4863     
4864             this.headerEditEl.dom.value = this.title;
4865             this.headerEditEl.removeClass('d-none');
4866             this.headerEditEl.dom.focus();
4867             this.titleEl.addClass('d-none');
4868             
4869             this.is_header_editing = true;
4870             return
4871         }
4872         // flip back to not editing.
4873         this.title = this.headerEditEl.dom.value;
4874         this.headerEditEl.addClass('d-none');
4875         this.titleEl.removeClass('d-none');
4876         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877         this.is_header_editing = false;
4878         this.fireEvent('titlechanged', this, this.title);
4879     
4880             
4881         
4882     }
4883
4884 });
4885
4886
4887 Roo.apply(Roo.bootstrap.Modal,  {
4888     /**
4889          * Button config that displays a single OK button
4890          * @type Object
4891          */
4892         OK :  [{
4893             name : 'ok',
4894             weight : 'primary',
4895             html : 'OK'
4896         }],
4897         /**
4898          * Button config that displays Yes and No buttons
4899          * @type Object
4900          */
4901         YESNO : [
4902             {
4903                 name  : 'no',
4904                 html : 'No'
4905             },
4906             {
4907                 name  :'yes',
4908                 weight : 'primary',
4909                 html : 'Yes'
4910             }
4911         ],
4912
4913         /**
4914          * Button config that displays OK and Cancel buttons
4915          * @type Object
4916          */
4917         OKCANCEL : [
4918             {
4919                name : 'cancel',
4920                 html : 'Cancel'
4921             },
4922             {
4923                 name : 'ok',
4924                 weight : 'primary',
4925                 html : 'OK'
4926             }
4927         ],
4928         /**
4929          * Button config that displays Yes, No and Cancel buttons
4930          * @type Object
4931          */
4932         YESNOCANCEL : [
4933             {
4934                 name : 'yes',
4935                 weight : 'primary',
4936                 html : 'Yes'
4937             },
4938             {
4939                 name : 'no',
4940                 html : 'No'
4941             },
4942             {
4943                 name : 'cancel',
4944                 html : 'Cancel'
4945             }
4946         ],
4947         
4948         zIndex : 10001
4949 });
4950
4951 /*
4952  * - LGPL
4953  *
4954  * messagebox - can be used as a replace
4955  * 
4956  */
4957 /**
4958  * @class Roo.MessageBox
4959  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4960  * Example usage:
4961  *<pre><code>
4962 // Basic alert:
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4964
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4967     if (btn == 'ok'){
4968         // process text value...
4969     }
4970 });
4971
4972 // Show a dialog using config options:
4973 Roo.Msg.show({
4974    title:'Save Changes?',
4975    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976    buttons: Roo.Msg.YESNOCANCEL,
4977    fn: processResult,
4978    animEl: 'elId'
4979 });
4980 </code></pre>
4981  * @static
4982  */
4983 Roo.bootstrap.MessageBox = function(){
4984     var dlg, opt, mask, waitTimer;
4985     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986     var buttons, activeTextEl, bwidth;
4987
4988     
4989     // private
4990     var handleButton = function(button){
4991         dlg.hide();
4992         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4993     };
4994
4995     // private
4996     var handleHide = function(){
4997         if(opt && opt.cls){
4998             dlg.el.removeClass(opt.cls);
4999         }
5000         //if(waitTimer){
5001         //    Roo.TaskMgr.stop(waitTimer);
5002         //    waitTimer = null;
5003         //}
5004     };
5005
5006     // private
5007     var updateButtons = function(b){
5008         var width = 0;
5009         if(!b){
5010             buttons["ok"].hide();
5011             buttons["cancel"].hide();
5012             buttons["yes"].hide();
5013             buttons["no"].hide();
5014             dlg.footerEl.hide();
5015             
5016             return width;
5017         }
5018         dlg.footerEl.show();
5019         for(var k in buttons){
5020             if(typeof buttons[k] != "function"){
5021                 if(b[k]){
5022                     buttons[k].show();
5023                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024                     width += buttons[k].el.getWidth()+15;
5025                 }else{
5026                     buttons[k].hide();
5027                 }
5028             }
5029         }
5030         return width;
5031     };
5032
5033     // private
5034     var handleEsc = function(d, k, e){
5035         if(opt && opt.closable !== false){
5036             dlg.hide();
5037         }
5038         if(e){
5039             e.stopEvent();
5040         }
5041     };
5042
5043     return {
5044         /**
5045          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046          * @return {Roo.BasicDialog} The BasicDialog element
5047          */
5048         getDialog : function(){
5049            if(!dlg){
5050                 dlg = new Roo.bootstrap.Modal( {
5051                     //draggable: true,
5052                     //resizable:false,
5053                     //constraintoviewport:false,
5054                     //fixedcenter:true,
5055                     //collapsible : false,
5056                     //shim:true,
5057                     //modal: true,
5058                 //    width: 'auto',
5059                   //  height:100,
5060                     //buttonAlign:"center",
5061                     closeClick : function(){
5062                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5063                             handleButton("no");
5064                         }else{
5065                             handleButton("cancel");
5066                         }
5067                     }
5068                 });
5069                 dlg.render();
5070                 dlg.on("hide", handleHide);
5071                 mask = dlg.mask;
5072                 //dlg.addKeyListener(27, handleEsc);
5073                 buttons = {};
5074                 this.buttons = buttons;
5075                 var bt = this.buttonText;
5076                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5080                 //Roo.log(buttons);
5081                 bodyEl = dlg.bodyEl.createChild({
5082
5083                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084                         '<textarea class="roo-mb-textarea"></textarea>' +
5085                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5086                 });
5087                 msgEl = bodyEl.dom.firstChild;
5088                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089                 textboxEl.enableDisplayMode();
5090                 textboxEl.addKeyListener([10,13], function(){
5091                     if(dlg.isVisible() && opt && opt.buttons){
5092                         if(opt.buttons.ok){
5093                             handleButton("ok");
5094                         }else if(opt.buttons.yes){
5095                             handleButton("yes");
5096                         }
5097                     }
5098                 });
5099                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100                 textareaEl.enableDisplayMode();
5101                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102                 progressEl.enableDisplayMode();
5103                 
5104                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105                 var pf = progressEl.dom.firstChild;
5106                 if (pf) {
5107                     pp = Roo.get(pf.firstChild);
5108                     pp.setHeight(pf.offsetHeight);
5109                 }
5110                 
5111             }
5112             return dlg;
5113         },
5114
5115         /**
5116          * Updates the message box body text
5117          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118          * the XHTML-compliant non-breaking space character '&amp;#160;')
5119          * @return {Roo.MessageBox} This message box
5120          */
5121         updateText : function(text)
5122         {
5123             if(!dlg.isVisible() && !opt.width){
5124                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5126             }
5127             msgEl.innerHTML = text || '&#160;';
5128       
5129             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5131             var w = Math.max(
5132                     Math.min(opt.width || cw , this.maxWidth), 
5133                     Math.max(opt.minWidth || this.minWidth, bwidth)
5134             );
5135             if(opt.prompt){
5136                 activeTextEl.setWidth(w);
5137             }
5138             if(dlg.isVisible()){
5139                 dlg.fixedcenter = false;
5140             }
5141             // to big, make it scroll. = But as usual stupid IE does not support
5142             // !important..
5143             
5144             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5147             } else {
5148                 bodyEl.dom.style.height = '';
5149                 bodyEl.dom.style.overflowY = '';
5150             }
5151             if (cw > w) {
5152                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5153             } else {
5154                 bodyEl.dom.style.overflowX = '';
5155             }
5156             
5157             dlg.setContentSize(w, bodyEl.getHeight());
5158             if(dlg.isVisible()){
5159                 dlg.fixedcenter = true;
5160             }
5161             return this;
5162         },
5163
5164         /**
5165          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5166          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169          * @return {Roo.MessageBox} This message box
5170          */
5171         updateProgress : function(value, text){
5172             if(text){
5173                 this.updateText(text);
5174             }
5175             
5176             if (pp) { // weird bug on my firefox - for some reason this is not defined
5177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5179             }
5180             return this;
5181         },        
5182
5183         /**
5184          * Returns true if the message box is currently displayed
5185          * @return {Boolean} True if the message box is visible, else false
5186          */
5187         isVisible : function(){
5188             return dlg && dlg.isVisible();  
5189         },
5190
5191         /**
5192          * Hides the message box if it is displayed
5193          */
5194         hide : function(){
5195             if(this.isVisible()){
5196                 dlg.hide();
5197             }  
5198         },
5199
5200         /**
5201          * Displays a new message box, or reinitializes an existing message box, based on the config options
5202          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203          * The following config object properties are supported:
5204          * <pre>
5205 Property    Type             Description
5206 ----------  ---------------  ------------------------------------------------------------------------------------
5207 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5208                                    closes (defaults to undefined)
5209 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5212                                    progress and wait dialogs will ignore this property and always hide the
5213                                    close button as they can only be closed programmatically.
5214 cls               String           A custom CSS class to apply to the message box element
5215 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5216                                    displayed (defaults to 75)
5217 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5218                                    function will be btn (the name of the button that was clicked, if applicable,
5219                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5220                                    Progress and wait dialogs will ignore this option since they do not respond to
5221                                    user actions and can only be closed programmatically, so any required function
5222                                    should be called by the same code after it closes the dialog.
5223 icon              String           A CSS class that provides a background image to be used as an icon for
5224                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5226 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5227 modal             Boolean          False to allow user interaction with the page while the message box is
5228                                    displayed (defaults to true)
5229 msg               String           A string that will replace the existing message box body text (defaults
5230                                    to the XHTML-compliant non-breaking space character '&#160;')
5231 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5232 progress          Boolean          True to display a progress bar (defaults to false)
5233 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5236 title             String           The title text
5237 value             String           The string value to set into the active textbox element if displayed
5238 wait              Boolean          True to display a progress bar (defaults to false)
5239 width             Number           The width of the dialog in pixels
5240 </pre>
5241          *
5242          * Example usage:
5243          * <pre><code>
5244 Roo.Msg.show({
5245    title: 'Address',
5246    msg: 'Please enter your address:',
5247    width: 300,
5248    buttons: Roo.MessageBox.OKCANCEL,
5249    multiline: true,
5250    fn: saveAddress,
5251    animEl: 'addAddressBtn'
5252 });
5253 </code></pre>
5254          * @param {Object} config Configuration options
5255          * @return {Roo.MessageBox} This message box
5256          */
5257         show : function(options)
5258         {
5259             
5260             // this causes nightmares if you show one dialog after another
5261             // especially on callbacks..
5262              
5263             if(this.isVisible()){
5264                 
5265                 this.hide();
5266                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5268                 Roo.log("New Dialog Message:" +  options.msg )
5269                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5271                 
5272             }
5273             var d = this.getDialog();
5274             opt = options;
5275             d.setTitle(opt.title || "&#160;");
5276             d.closeEl.setDisplayed(opt.closable !== false);
5277             activeTextEl = textboxEl;
5278             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5279             if(opt.prompt){
5280                 if(opt.multiline){
5281                     textboxEl.hide();
5282                     textareaEl.show();
5283                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5284                         opt.multiline : this.defaultTextHeight);
5285                     activeTextEl = textareaEl;
5286                 }else{
5287                     textboxEl.show();
5288                     textareaEl.hide();
5289                 }
5290             }else{
5291                 textboxEl.hide();
5292                 textareaEl.hide();
5293             }
5294             progressEl.setDisplayed(opt.progress === true);
5295             if (opt.progress) {
5296                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5297             }
5298             this.updateProgress(0);
5299             activeTextEl.dom.value = opt.value || "";
5300             if(opt.prompt){
5301                 dlg.setDefaultButton(activeTextEl);
5302             }else{
5303                 var bs = opt.buttons;
5304                 var db = null;
5305                 if(bs && bs.ok){
5306                     db = buttons["ok"];
5307                 }else if(bs && bs.yes){
5308                     db = buttons["yes"];
5309                 }
5310                 dlg.setDefaultButton(db);
5311             }
5312             bwidth = updateButtons(opt.buttons);
5313             this.updateText(opt.msg);
5314             if(opt.cls){
5315                 d.el.addClass(opt.cls);
5316             }
5317             d.proxyDrag = opt.proxyDrag === true;
5318             d.modal = opt.modal !== false;
5319             d.mask = opt.modal !== false ? mask : false;
5320             if(!d.isVisible()){
5321                 // force it to the end of the z-index stack so it gets a cursor in FF
5322                 document.body.appendChild(dlg.el.dom);
5323                 d.animateTarget = null;
5324                 d.show(options.animEl);
5325             }
5326             return this;
5327         },
5328
5329         /**
5330          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5331          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332          * and closing the message box when the process is complete.
5333          * @param {String} title The title bar text
5334          * @param {String} msg The message box body text
5335          * @return {Roo.MessageBox} This message box
5336          */
5337         progress : function(title, msg){
5338             this.show({
5339                 title : title,
5340                 msg : msg,
5341                 buttons: false,
5342                 progress:true,
5343                 closable:false,
5344                 minWidth: this.minProgressWidth,
5345                 modal : true
5346             });
5347             return this;
5348         },
5349
5350         /**
5351          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352          * If a callback function is passed it will be called after the user clicks the button, and the
5353          * id of the button that was clicked will be passed as the only parameter to the callback
5354          * (could also be the top-right close button).
5355          * @param {String} title The title bar text
5356          * @param {String} msg The message box body text
5357          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358          * @param {Object} scope (optional) The scope of the callback function
5359          * @return {Roo.MessageBox} This message box
5360          */
5361         alert : function(title, msg, fn, scope)
5362         {
5363             this.show({
5364                 title : title,
5365                 msg : msg,
5366                 buttons: this.OK,
5367                 fn: fn,
5368                 closable : false,
5369                 scope : scope,
5370                 modal : true
5371             });
5372             return this;
5373         },
5374
5375         /**
5376          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5377          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378          * You are responsible for closing the message box when the process is complete.
5379          * @param {String} msg The message box body text
5380          * @param {String} title (optional) The title bar text
5381          * @return {Roo.MessageBox} This message box
5382          */
5383         wait : function(msg, title){
5384             this.show({
5385                 title : title,
5386                 msg : msg,
5387                 buttons: false,
5388                 closable:false,
5389                 progress:true,
5390                 modal:true,
5391                 width:300,
5392                 wait:true
5393             });
5394             waitTimer = Roo.TaskMgr.start({
5395                 run: function(i){
5396                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5397                 },
5398                 interval: 1000
5399             });
5400             return this;
5401         },
5402
5403         /**
5404          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407          * @param {String} title The title bar text
5408          * @param {String} msg The message box body text
5409          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410          * @param {Object} scope (optional) The scope of the callback function
5411          * @return {Roo.MessageBox} This message box
5412          */
5413         confirm : function(title, msg, fn, scope){
5414             this.show({
5415                 title : title,
5416                 msg : msg,
5417                 buttons: this.YESNO,
5418                 fn: fn,
5419                 scope : scope,
5420                 modal : true
5421             });
5422             return this;
5423         },
5424
5425         /**
5426          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5428          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429          * (could also be the top-right close button) and the text that was entered will be passed as the two
5430          * parameters to the callback.
5431          * @param {String} title The title bar text
5432          * @param {String} msg The message box body text
5433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434          * @param {Object} scope (optional) The scope of the callback function
5435          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437          * @return {Roo.MessageBox} This message box
5438          */
5439         prompt : function(title, msg, fn, scope, multiline){
5440             this.show({
5441                 title : title,
5442                 msg : msg,
5443                 buttons: this.OKCANCEL,
5444                 fn: fn,
5445                 minWidth:250,
5446                 scope : scope,
5447                 prompt:true,
5448                 multiline: multiline,
5449                 modal : true
5450             });
5451             return this;
5452         },
5453
5454         /**
5455          * Button config that displays a single OK button
5456          * @type Object
5457          */
5458         OK : {ok:true},
5459         /**
5460          * Button config that displays Yes and No buttons
5461          * @type Object
5462          */
5463         YESNO : {yes:true, no:true},
5464         /**
5465          * Button config that displays OK and Cancel buttons
5466          * @type Object
5467          */
5468         OKCANCEL : {ok:true, cancel:true},
5469         /**
5470          * Button config that displays Yes, No and Cancel buttons
5471          * @type Object
5472          */
5473         YESNOCANCEL : {yes:true, no:true, cancel:true},
5474
5475         /**
5476          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5477          * @type Number
5478          */
5479         defaultTextHeight : 75,
5480         /**
5481          * The maximum width in pixels of the message box (defaults to 600)
5482          * @type Number
5483          */
5484         maxWidth : 600,
5485         /**
5486          * The minimum width in pixels of the message box (defaults to 100)
5487          * @type Number
5488          */
5489         minWidth : 100,
5490         /**
5491          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5492          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5493          * @type Number
5494          */
5495         minProgressWidth : 250,
5496         /**
5497          * An object containing the default button text strings that can be overriden for localized language support.
5498          * Supported properties are: ok, cancel, yes and no.
5499          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5500          * @type Object
5501          */
5502         buttonText : {
5503             ok : "OK",
5504             cancel : "Cancel",
5505             yes : "Yes",
5506             no : "No"
5507         }
5508     };
5509 }();
5510
5511 /**
5512  * Shorthand for {@link Roo.MessageBox}
5513  */
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 /*
5517  * - LGPL
5518  *
5519  * navbar
5520  * 
5521  */
5522
5523 /**
5524  * @class Roo.bootstrap.nav.Bar
5525  * @extends Roo.bootstrap.Component
5526  * @abstract
5527  * Bootstrap Navbar class
5528
5529  * @constructor
5530  * Create a new Navbar
5531  * @param {Object} config The config object
5532  */
5533
5534
5535 Roo.bootstrap.nav.Bar = function(config){
5536     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5537     this.addEvents({
5538         // raw events
5539         /**
5540          * @event beforetoggle
5541          * Fire before toggle the menu
5542          * @param {Roo.EventObject} e
5543          */
5544         "beforetoggle" : true
5545     });
5546 };
5547
5548 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5549     
5550     
5551    
5552     // private
5553     navItems : false,
5554     loadMask : false,
5555     
5556     
5557     getAutoCreate : function(){
5558         
5559         
5560         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5561         
5562     },
5563     
5564     initEvents :function ()
5565     {
5566         //Roo.log(this.el.select('.navbar-toggle',true));
5567         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5568         
5569         var mark = {
5570             tag: "div",
5571             cls:"x-dlg-mask"
5572         };
5573         
5574         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5575         
5576         var size = this.el.getSize();
5577         this.maskEl.setSize(size.width, size.height);
5578         this.maskEl.enableDisplayMode("block");
5579         this.maskEl.hide();
5580         
5581         if(this.loadMask){
5582             this.maskEl.show();
5583         }
5584     },
5585     
5586     
5587     getChildContainer : function()
5588     {
5589         if (this.el && this.el.select('.collapse').getCount()) {
5590             return this.el.select('.collapse',true).first();
5591         }
5592         
5593         return this.el;
5594     },
5595     
5596     mask : function()
5597     {
5598         this.maskEl.show();
5599     },
5600     
5601     unmask : function()
5602     {
5603         this.maskEl.hide();
5604     },
5605     onToggle : function()
5606     {
5607         
5608         if(this.fireEvent('beforetoggle', this) === false){
5609             return;
5610         }
5611         var ce = this.el.select('.navbar-collapse',true).first();
5612       
5613         if (!ce.hasClass('show')) {
5614            this.expand();
5615         } else {
5616             this.collapse();
5617         }
5618         
5619         
5620     
5621     },
5622     /**
5623      * Expand the navbar pulldown 
5624      */
5625     expand : function ()
5626     {
5627        
5628         var ce = this.el.select('.navbar-collapse',true).first();
5629         if (ce.hasClass('collapsing')) {
5630             return;
5631         }
5632         ce.dom.style.height = '';
5633                // show it...
5634         ce.addClass('in'); // old...
5635         ce.removeClass('collapse');
5636         ce.addClass('show');
5637         var h = ce.getHeight();
5638         Roo.log(h);
5639         ce.removeClass('show');
5640         // at this point we should be able to see it..
5641         ce.addClass('collapsing');
5642         
5643         ce.setHeight(0); // resize it ...
5644         ce.on('transitionend', function() {
5645             //Roo.log('done transition');
5646             ce.removeClass('collapsing');
5647             ce.addClass('show');
5648             ce.removeClass('collapse');
5649
5650             ce.dom.style.height = '';
5651         }, this, { single: true} );
5652         ce.setHeight(h);
5653         ce.dom.scrollTop = 0;
5654     },
5655     /**
5656      * Collapse the navbar pulldown 
5657      */
5658     collapse : function()
5659     {
5660          var ce = this.el.select('.navbar-collapse',true).first();
5661        
5662         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5663             // it's collapsed or collapsing..
5664             return;
5665         }
5666         ce.removeClass('in'); // old...
5667         ce.setHeight(ce.getHeight());
5668         ce.removeClass('show');
5669         ce.addClass('collapsing');
5670         
5671         ce.on('transitionend', function() {
5672             ce.dom.style.height = '';
5673             ce.removeClass('collapsing');
5674             ce.addClass('collapse');
5675         }, this, { single: true} );
5676         ce.setHeight(0);
5677     }
5678     
5679     
5680     
5681 });
5682
5683
5684
5685  
5686
5687  /*
5688  * - LGPL
5689  *
5690  * navbar
5691  * 
5692  */
5693
5694 /**
5695  * @class Roo.bootstrap.nav.Simplebar
5696  * @extends Roo.bootstrap.nav.Bar
5697  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5698  * Bootstrap Sidebar class
5699  *
5700  * @cfg {Boolean} inverse is inverted color
5701  * 
5702  * @cfg {String} type (nav | pills | tabs)
5703  * @cfg {Boolean} arrangement stacked | justified
5704  * @cfg {String} align (left | right) alignment
5705  * 
5706  * @cfg {Boolean} main (true|false) main nav bar? default false
5707  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5708  * 
5709  * @cfg {String} tag (header|footer|nav|div) default is nav 
5710
5711  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5712  * 
5713  * 
5714  * @constructor
5715  * Create a new Sidebar
5716  * @param {Object} config The config object
5717  */
5718
5719
5720 Roo.bootstrap.nav.Simplebar = function(config){
5721     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5722 };
5723
5724 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5725     
5726     inverse: false,
5727     
5728     type: false,
5729     arrangement: '',
5730     align : false,
5731     
5732     weight : 'light',
5733     
5734     main : false,
5735     
5736     
5737     tag : false,
5738     
5739     
5740     getAutoCreate : function(){
5741         
5742         
5743         var cfg = {
5744             tag : this.tag || 'div',
5745             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5746         };
5747         if (['light','white'].indexOf(this.weight) > -1) {
5748             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5749         }
5750         cfg.cls += ' bg-' + this.weight;
5751         
5752         if (this.inverse) {
5753             cfg.cls += ' navbar-inverse';
5754             
5755         }
5756         
5757         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5758         
5759         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5760             return cfg;
5761         }
5762         
5763         
5764     
5765         
5766         cfg.cn = [
5767             {
5768                 cls: 'nav nav-' + this.xtype,
5769                 tag : 'ul'
5770             }
5771         ];
5772         
5773          
5774         this.type = this.type || 'nav';
5775         if (['tabs','pills'].indexOf(this.type) != -1) {
5776             cfg.cn[0].cls += ' nav-' + this.type
5777         
5778         
5779         } else {
5780             if (this.type!=='nav') {
5781                 Roo.log('nav type must be nav/tabs/pills')
5782             }
5783             cfg.cn[0].cls += ' navbar-nav'
5784         }
5785         
5786         
5787         
5788         
5789         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790             cfg.cn[0].cls += ' nav-' + this.arrangement;
5791         }
5792         
5793         
5794         if (this.align === 'right') {
5795             cfg.cn[0].cls += ' navbar-right';
5796         }
5797         
5798         
5799         
5800         
5801         return cfg;
5802     
5803         
5804     }
5805     
5806     
5807     
5808 });
5809
5810
5811
5812  
5813
5814  
5815        /*
5816  * - LGPL
5817  *
5818  * navbar
5819  * navbar-fixed-top
5820  * navbar-expand-md  fixed-top 
5821  */
5822
5823 /**
5824  * @class Roo.bootstrap.nav.Headerbar
5825  * @extends Roo.bootstrap.nav.Simplebar
5826  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5827  * Bootstrap Sidebar class
5828  *
5829  * @cfg {String} brand what is brand
5830  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5831  * @cfg {String} brand_href href of the brand
5832  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5833  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5834  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5835  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5836  * 
5837  * @constructor
5838  * Create a new Sidebar
5839  * @param {Object} config The config object
5840  */
5841
5842
5843 Roo.bootstrap.nav.Headerbar = function(config){
5844     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5845       
5846 };
5847
5848 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5849     
5850     position: '',
5851     brand: '',
5852     brand_href: false,
5853     srButton : true,
5854     autohide : false,
5855     desktopCenter : false,
5856    
5857     
5858     getAutoCreate : function(){
5859         
5860         var   cfg = {
5861             tag: this.nav || 'nav',
5862             cls: 'navbar navbar-expand-md',
5863             role: 'navigation',
5864             cn: []
5865         };
5866         
5867         var cn = cfg.cn;
5868         if (this.desktopCenter) {
5869             cn.push({cls : 'container', cn : []});
5870             cn = cn[0].cn;
5871         }
5872         
5873         if(this.srButton){
5874             var btn = {
5875                 tag: 'button',
5876                 type: 'button',
5877                 cls: 'navbar-toggle navbar-toggler',
5878                 'data-toggle': 'collapse',
5879                 cn: [
5880                     {
5881                         tag: 'span',
5882                         cls: 'sr-only',
5883                         html: 'Toggle navigation'
5884                     },
5885                     {
5886                         tag: 'span',
5887                         cls: 'icon-bar navbar-toggler-icon'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     }
5897                 ]
5898             };
5899             
5900             cn.push( Roo.bootstrap.version == 4 ? btn : {
5901                 tag: 'div',
5902                 cls: 'navbar-header',
5903                 cn: [
5904                     btn
5905                 ]
5906             });
5907         }
5908         
5909         cn.push({
5910             tag: 'div',
5911             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5912             cn : []
5913         });
5914         
5915         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5916         
5917         if (['light','white'].indexOf(this.weight) > -1) {
5918             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5919         }
5920         cfg.cls += ' bg-' + this.weight;
5921         
5922         
5923         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5924             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5925             
5926             // tag can override this..
5927             
5928             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5929         }
5930         
5931         if (this.brand !== '') {
5932             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5933             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5934                 tag: 'a',
5935                 href: this.brand_href ? this.brand_href : '#',
5936                 cls: 'navbar-brand',
5937                 cn: [
5938                 this.brand
5939                 ]
5940             });
5941         }
5942         
5943         if(this.main){
5944             cfg.cls += ' main-nav';
5945         }
5946         
5947         
5948         return cfg;
5949
5950         
5951     },
5952     getHeaderChildContainer : function()
5953     {
5954         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5955             return this.el.select('.navbar-header',true).first();
5956         }
5957         
5958         return this.getChildContainer();
5959     },
5960     
5961     getChildContainer : function()
5962     {
5963          
5964         return this.el.select('.roo-navbar-collapse',true).first();
5965          
5966         
5967     },
5968     
5969     initEvents : function()
5970     {
5971         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5972         
5973         if (this.autohide) {
5974             
5975             var prevScroll = 0;
5976             var ft = this.el;
5977             
5978             Roo.get(document).on('scroll',function(e) {
5979                 var ns = Roo.get(document).getScroll().top;
5980                 var os = prevScroll;
5981                 prevScroll = ns;
5982                 
5983                 if(ns > os){
5984                     ft.removeClass('slideDown');
5985                     ft.addClass('slideUp');
5986                     return;
5987                 }
5988                 ft.removeClass('slideUp');
5989                 ft.addClass('slideDown');
5990                  
5991               
5992           },this);
5993         }
5994     }    
5995     
5996 });
5997
5998
5999
6000  
6001
6002  /*
6003  * - LGPL
6004  *
6005  * navbar
6006  * 
6007  */
6008
6009 /**
6010  * @class Roo.bootstrap.nav.Sidebar
6011  * @extends Roo.bootstrap.nav.Bar
6012  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6013  * Bootstrap Sidebar class
6014  * 
6015  * @constructor
6016  * Create a new Sidebar
6017  * @param {Object} config The config object
6018  */
6019
6020
6021 Roo.bootstrap.nav.Sidebar = function(config){
6022     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6023 };
6024
6025 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6026     
6027     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6028     
6029     getAutoCreate : function(){
6030         
6031         
6032         return  {
6033             tag: 'div',
6034             cls: 'sidebar sidebar-nav'
6035         };
6036     
6037         
6038     }
6039     
6040     
6041     
6042 });
6043
6044
6045
6046  
6047
6048  /*
6049  * - LGPL
6050  *
6051  * nav group
6052  * 
6053  */
6054
6055 /**
6056  * @class Roo.bootstrap.nav.Group
6057  * @extends Roo.bootstrap.Component
6058  * @children Roo.bootstrap.nav.Item
6059  * Bootstrap NavGroup class
6060  * @cfg {String} align (left|right)
6061  * @cfg {Boolean} inverse
6062  * @cfg {String} type (nav|pills|tab) default nav
6063  * @cfg {String} navId - reference Id for navbar.
6064  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6065  * 
6066  * @constructor
6067  * Create a new nav group
6068  * @param {Object} config The config object
6069  */
6070
6071 Roo.bootstrap.nav.Group = function(config){
6072     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6073     this.navItems = [];
6074    
6075     Roo.bootstrap.nav.Group.register(this);
6076      this.addEvents({
6077         /**
6078              * @event changed
6079              * Fires when the active item changes
6080              * @param {Roo.bootstrap.nav.Group} this
6081              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6083          */
6084         'changed': true
6085      });
6086     
6087 };
6088
6089 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6090     
6091     align: '',
6092     inverse: false,
6093     form: false,
6094     type: 'nav',
6095     navId : '',
6096     // private
6097     pilltype : true,
6098     
6099     navItems : false, 
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag : 'ul',
6107             cls: 'nav' 
6108         };
6109         if (Roo.bootstrap.version == 4) {
6110             if (['tabs','pills'].indexOf(this.type) != -1) {
6111                 cfg.cls += ' nav-' + this.type; 
6112             } else {
6113                 // trying to remove so header bar can right align top?
6114                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115                     // do not use on header bar... 
6116                     cfg.cls += ' navbar-nav';
6117                 }
6118             }
6119             
6120         } else {
6121             if (['tabs','pills'].indexOf(this.type) != -1) {
6122                 cfg.cls += ' nav-' + this.type
6123             } else {
6124                 if (this.type !== 'nav') {
6125                     Roo.log('nav type must be nav/tabs/pills')
6126                 }
6127                 cfg.cls += ' navbar-nav'
6128             }
6129         }
6130         
6131         if (this.parent() && this.parent().sidebar) {
6132             cfg = {
6133                 tag: 'ul',
6134                 cls: 'dashboard-menu sidebar-menu'
6135             };
6136             
6137             return cfg;
6138         }
6139         
6140         if (this.form === true) {
6141             cfg = {
6142                 tag: 'form',
6143                 cls: 'navbar-form form-inline'
6144             };
6145             //nav navbar-right ml-md-auto
6146             if (this.align === 'right') {
6147                 cfg.cls += ' navbar-right ml-md-auto';
6148             } else {
6149                 cfg.cls += ' navbar-left';
6150             }
6151         }
6152         
6153         if (this.align === 'right') {
6154             cfg.cls += ' navbar-right ml-md-auto';
6155         } else {
6156             cfg.cls += ' mr-auto';
6157         }
6158         
6159         if (this.inverse) {
6160             cfg.cls += ' navbar-inverse';
6161             
6162         }
6163         
6164         
6165         return cfg;
6166     },
6167     /**
6168     * sets the active Navigation item
6169     * @param {Roo.bootstrap.nav.Item} the new current navitem
6170     */
6171     setActiveItem : function(item)
6172     {
6173         var prev = false;
6174         Roo.each(this.navItems, function(v){
6175             if (v == item) {
6176                 return ;
6177             }
6178             if (v.isActive()) {
6179                 v.setActive(false, true);
6180                 prev = v;
6181                 
6182             }
6183             
6184         });
6185
6186         item.setActive(true, true);
6187         this.fireEvent('changed', this, item, prev);
6188         
6189         
6190     },
6191     /**
6192     * gets the active Navigation item
6193     * @return {Roo.bootstrap.nav.Item} the current navitem
6194     */
6195     getActive : function()
6196     {
6197         
6198         var prev = false;
6199         Roo.each(this.navItems, function(v){
6200             
6201             if (v.isActive()) {
6202                 prev = v;
6203                 
6204             }
6205             
6206         });
6207         return prev;
6208     },
6209     
6210     indexOfNav : function()
6211     {
6212         
6213         var prev = false;
6214         Roo.each(this.navItems, function(v,i){
6215             
6216             if (v.isActive()) {
6217                 prev = i;
6218                 
6219             }
6220             
6221         });
6222         return prev;
6223     },
6224     /**
6225     * adds a Navigation item
6226     * @param {Roo.bootstrap.nav.Item} the navitem to add
6227     */
6228     addItem : function(cfg)
6229     {
6230         if (this.form && Roo.bootstrap.version == 4) {
6231             cfg.tag = 'div';
6232         }
6233         var cn = new Roo.bootstrap.nav.Item(cfg);
6234         this.register(cn);
6235         cn.parentId = this.id;
6236         cn.onRender(this.el, null);
6237         return cn;
6238     },
6239     /**
6240     * register a Navigation item
6241     * @param {Roo.bootstrap.nav.Item} the navitem to add
6242     */
6243     register : function(item)
6244     {
6245         this.navItems.push( item);
6246         item.navId = this.navId;
6247     
6248     },
6249     
6250     /**
6251     * clear all the Navigation item
6252     */
6253    
6254     clearAll : function()
6255     {
6256         this.navItems = [];
6257         this.el.dom.innerHTML = '';
6258     },
6259     
6260     getNavItem: function(tabId)
6261     {
6262         var ret = false;
6263         Roo.each(this.navItems, function(e) {
6264             if (e.tabId == tabId) {
6265                ret =  e;
6266                return false;
6267             }
6268             return true;
6269             
6270         });
6271         return ret;
6272     },
6273     
6274     setActiveNext : function()
6275     {
6276         var i = this.indexOfNav(this.getActive());
6277         if (i > this.navItems.length) {
6278             return;
6279         }
6280         this.setActiveItem(this.navItems[i+1]);
6281     },
6282     setActivePrev : function()
6283     {
6284         var i = this.indexOfNav(this.getActive());
6285         if (i  < 1) {
6286             return;
6287         }
6288         this.setActiveItem(this.navItems[i-1]);
6289     },
6290     clearWasActive : function(except) {
6291         Roo.each(this.navItems, function(e) {
6292             if (e.tabId != except.tabId && e.was_active) {
6293                e.was_active = false;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299     },
6300     getWasActive : function ()
6301     {
6302         var r = false;
6303         Roo.each(this.navItems, function(e) {
6304             if (e.was_active) {
6305                r = e;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311         return r;
6312     }
6313     
6314     
6315 });
6316
6317  
6318 Roo.apply(Roo.bootstrap.nav.Group, {
6319     
6320     groups: {},
6321      /**
6322     * register a Navigation Group
6323     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6324     */
6325     register : function(navgrp)
6326     {
6327         this.groups[navgrp.navId] = navgrp;
6328         
6329     },
6330     /**
6331     * fetch a Navigation Group based on the navigation ID
6332     * @param {string} the navgroup to add
6333     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6334     */
6335     get: function(navId) {
6336         if (typeof(this.groups[navId]) == 'undefined') {
6337             return false;
6338             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6339         }
6340         return this.groups[navId] ;
6341     }
6342     
6343     
6344     
6345 });
6346
6347  /**
6348  * @class Roo.bootstrap.nav.Item
6349  * @extends Roo.bootstrap.Component
6350  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351  * @parent Roo.bootstrap.nav.Group
6352  * @licence LGPL
6353  * Bootstrap Navbar.NavItem class
6354  * 
6355  * @cfg {String} href  link to
6356  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357  * @cfg {Boolean} button_outline show and outlined button
6358  * @cfg {String} html content of button
6359  * @cfg {String} badge text inside badge
6360  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361  * @cfg {String} glyphicon DEPRICATED - use fa
6362  * @cfg {String} icon DEPRICATED - use fa
6363  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364  * @cfg {Boolean} active Is item active
6365  * @cfg {Boolean} disabled Is item disabled
6366  * @cfg {String} linkcls  Link Class
6367  * @cfg {Boolean} preventDefault (true | false) default false
6368  * @cfg {String} tabId the tab that this item activates.
6369  * @cfg {String} tagtype (a|span) render as a href or span?
6370  * @cfg {Boolean} animateRef (true|false) link to element default false  
6371  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6372   
6373  * @constructor
6374  * Create a new Navbar Item
6375  * @param {Object} config The config object
6376  */
6377 Roo.bootstrap.nav.Item = function(config){
6378     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6379     this.addEvents({
6380         // raw events
6381         /**
6382          * @event click
6383          * The raw click event for the entire grid.
6384          * @param {Roo.EventObject} e
6385          */
6386         "click" : true,
6387          /**
6388             * @event changed
6389             * Fires when the active item active state changes
6390             * @param {Roo.bootstrap.nav.Item} this
6391             * @param {boolean} state the new state
6392              
6393          */
6394         'changed': true,
6395         /**
6396             * @event scrollto
6397             * Fires when scroll to element
6398             * @param {Roo.bootstrap.nav.Item} this
6399             * @param {Object} options
6400             * @param {Roo.EventObject} e
6401              
6402          */
6403         'scrollto': true
6404     });
6405    
6406 };
6407
6408 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6409     
6410     href: false,
6411     html: '',
6412     badge: '',
6413     icon: false,
6414     fa : false,
6415     glyphicon: false,
6416     active: false,
6417     preventDefault : false,
6418     tabId : false,
6419     tagtype : 'a',
6420     tag: 'li',
6421     disabled : false,
6422     animateRef : false,
6423     was_active : false,
6424     button_weight : '',
6425     button_outline : false,
6426     linkcls : '',
6427     navLink: false,
6428     
6429     getAutoCreate : function(){
6430          
6431         var cfg = {
6432             tag: this.tag,
6433             cls: 'nav-item'
6434         };
6435         
6436         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6437         
6438         if (this.active) {
6439             cfg.cls +=  ' active' ;
6440         }
6441         if (this.disabled) {
6442             cfg.cls += ' disabled';
6443         }
6444         
6445         // BS4 only?
6446         if (this.button_weight.length) {
6447             cfg.tag = this.href ? 'a' : 'button';
6448             cfg.html = this.html || '';
6449             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6450             if (this.href) {
6451                 cfg.href = this.href;
6452             }
6453             if (this.fa) {
6454                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6455             } else {
6456                 cfg.cls += " nav-html";
6457             }
6458             
6459             // menu .. should add dropdown-menu class - so no need for carat..
6460             
6461             if (this.badge !== '') {
6462                  
6463                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6464             }
6465             return cfg;
6466         }
6467         
6468         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6469             cfg.cn = [
6470                 {
6471                     tag: this.tagtype,
6472                     href : this.href || "#",
6473                     html: this.html || '',
6474                     cls : ''
6475                 }
6476             ];
6477             if (this.tagtype == 'a') {
6478                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6479         
6480             }
6481             if (this.icon) {
6482                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483             } else  if (this.fa) {
6484                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else if(this.glyphicon) {
6486                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6487             } else {
6488                 cfg.cn[0].cls += " nav-html";
6489             }
6490             
6491             if (this.menu) {
6492                 cfg.cn[0].html += " <span class='caret'></span>";
6493              
6494             }
6495             
6496             if (this.badge !== '') {
6497                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6498             }
6499         }
6500         
6501         
6502         
6503         return cfg;
6504     },
6505     onRender : function(ct, position)
6506     {
6507        // Roo.log("Call onRender: " + this.xtype);
6508         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6509             this.tag = 'div';
6510         }
6511         
6512         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6513         this.navLink = this.el.select('.nav-link',true).first();
6514         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6515         return ret;
6516     },
6517       
6518     
6519     initEvents: function() 
6520     {
6521         if (typeof (this.menu) != 'undefined') {
6522             this.menu.parentType = this.xtype;
6523             this.menu.triggerEl = this.el;
6524             this.menu = this.addxtype(Roo.apply({}, this.menu));
6525         }
6526         
6527         this.el.on('click', this.onClick, this);
6528         
6529         //if(this.tagtype == 'span'){
6530         //    this.el.select('span',true).on('click', this.onClick, this);
6531         //}
6532        
6533         // at this point parent should be available..
6534         this.parent().register(this);
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if (e.getTarget('.dropdown-menu-item')) {
6540             // did you click on a menu itemm.... - then don't trigger onclick..
6541             return;
6542         }
6543         
6544         if(
6545                 this.preventDefault || 
6546                 this.href == '#' 
6547         ){
6548             Roo.log("NavItem - prevent Default?");
6549             e.preventDefault();
6550         }
6551         
6552         if (this.disabled) {
6553             return;
6554         }
6555         
6556         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6557         if (tg && tg.transition) {
6558             Roo.log("waiting for the transitionend");
6559             return;
6560         }
6561         
6562         
6563         
6564         //Roo.log("fire event clicked");
6565         if(this.fireEvent('click', this, e) === false){
6566             return;
6567         };
6568         
6569         if(this.tagtype == 'span'){
6570             return;
6571         }
6572         
6573         //Roo.log(this.href);
6574         var ael = this.el.select('a',true).first();
6575         //Roo.log(ael);
6576         
6577         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6578             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6579             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6580                 return; // ignore... - it's a 'hash' to another page.
6581             }
6582             Roo.log("NavItem - prevent Default?");
6583             e.preventDefault();
6584             this.scrollToElement(e);
6585         }
6586         
6587         
6588         var p =  this.parent();
6589    
6590         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6591             if (typeof(p.setActiveItem) !== 'undefined') {
6592                 p.setActiveItem(this);
6593             }
6594         }
6595         
6596         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6597         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6598             // remove the collapsed menu expand...
6599             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6600         }
6601     },
6602     
6603     isActive: function () {
6604         return this.active
6605     },
6606     setActive : function(state, fire, is_was_active)
6607     {
6608         if (this.active && !state && this.navId) {
6609             this.was_active = true;
6610             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6611             if (nv) {
6612                 nv.clearWasActive(this);
6613             }
6614             
6615         }
6616         this.active = state;
6617         
6618         if (!state ) {
6619             this.el.removeClass('active');
6620             this.navLink ? this.navLink.removeClass('active') : false;
6621         } else if (!this.el.hasClass('active')) {
6622             
6623             this.el.addClass('active');
6624             if (Roo.bootstrap.version == 4 && this.navLink ) {
6625                 this.navLink.addClass('active');
6626             }
6627             
6628         }
6629         if (fire) {
6630             this.fireEvent('changed', this, state);
6631         }
6632         
6633         // show a panel if it's registered and related..
6634         
6635         if (!this.navId || !this.tabId || !state || is_was_active) {
6636             return;
6637         }
6638         
6639         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6640         if (!tg) {
6641             return;
6642         }
6643         var pan = tg.getPanelByName(this.tabId);
6644         if (!pan) {
6645             return;
6646         }
6647         // if we can not flip to new panel - go back to old nav highlight..
6648         if (false == tg.showPanel(pan)) {
6649             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6650             if (nv) {
6651                 var onav = nv.getWasActive();
6652                 if (onav) {
6653                     onav.setActive(true, false, true);
6654                 }
6655             }
6656             
6657         }
6658         
6659         
6660         
6661     },
6662      // this should not be here...
6663     setDisabled : function(state)
6664     {
6665         this.disabled = state;
6666         if (!state ) {
6667             this.el.removeClass('disabled');
6668         } else if (!this.el.hasClass('disabled')) {
6669             this.el.addClass('disabled');
6670         }
6671         
6672     },
6673     
6674     /**
6675      * Fetch the element to display the tooltip on.
6676      * @return {Roo.Element} defaults to this.el
6677      */
6678     tooltipEl : function()
6679     {
6680         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6681     },
6682     
6683     scrollToElement : function(e)
6684     {
6685         var c = document.body;
6686         
6687         /*
6688          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6689          */
6690         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6691             c = document.documentElement;
6692         }
6693         
6694         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6695         
6696         if(!target){
6697             return;
6698         }
6699
6700         var o = target.calcOffsetsTo(c);
6701         
6702         var options = {
6703             target : target,
6704             value : o[1]
6705         };
6706         
6707         this.fireEvent('scrollto', this, options, e);
6708         
6709         Roo.get(c).scrollTo('top', options.value, true);
6710         
6711         return;
6712     },
6713     /**
6714      * Set the HTML (text content) of the item
6715      * @param {string} html  content for the nav item
6716      */
6717     setHtml : function(html)
6718     {
6719         this.html = html;
6720         this.htmlEl.dom.innerHTML = html;
6721         
6722     } 
6723 });
6724  
6725
6726  /*
6727  * - LGPL
6728  *
6729  * sidebar item
6730  *
6731  *  li
6732  *    <span> icon </span>
6733  *    <span> text </span>
6734  *    <span>badge </span>
6735  */
6736
6737 /**
6738  * @class Roo.bootstrap.nav.SidebarItem
6739  * @extends Roo.bootstrap.nav.Item
6740  * Bootstrap Navbar.NavSidebarItem class
6741  * 
6742  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6743  * {Boolean} open is the menu open
6744  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6745  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6746  * {String} buttonSize (sm|md|lg)the extra classes for the button
6747  * {Boolean} showArrow show arrow next to the text (default true)
6748  * @constructor
6749  * Create a new Navbar Button
6750  * @param {Object} config The config object
6751  */
6752 Roo.bootstrap.nav.SidebarItem = function(config){
6753     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6754     this.addEvents({
6755         // raw events
6756         /**
6757          * @event click
6758          * The raw click event for the entire grid.
6759          * @param {Roo.EventObject} e
6760          */
6761         "click" : true,
6762          /**
6763             * @event changed
6764             * Fires when the active item active state changes
6765             * @param {Roo.bootstrap.nav.SidebarItem} this
6766             * @param {boolean} state the new state
6767              
6768          */
6769         'changed': true
6770     });
6771    
6772 };
6773
6774 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6775     
6776     badgeWeight : 'default',
6777     
6778     open: false,
6779     
6780     buttonView : false,
6781     
6782     buttonWeight : 'default',
6783     
6784     buttonSize : 'md',
6785     
6786     showArrow : true,
6787     
6788     getAutoCreate : function(){
6789         
6790         
6791         var a = {
6792                 tag: 'a',
6793                 href : this.href || '#',
6794                 cls: '',
6795                 html : '',
6796                 cn : []
6797         };
6798         
6799         if(this.buttonView){
6800             a = {
6801                 tag: 'button',
6802                 href : this.href || '#',
6803                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804                 html : this.html,
6805                 cn : []
6806             };
6807         }
6808         
6809         var cfg = {
6810             tag: 'li',
6811             cls: '',
6812             cn: [ a ]
6813         };
6814         
6815         if (this.active) {
6816             cfg.cls += ' active';
6817         }
6818         
6819         if (this.disabled) {
6820             cfg.cls += ' disabled';
6821         }
6822         if (this.open) {
6823             cfg.cls += ' open x-open';
6824         }
6825         // left icon..
6826         if (this.glyphicon || this.icon) {
6827             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6828             a.cn.push({ tag : 'i', cls : c }) ;
6829         }
6830         
6831         if(!this.buttonView){
6832             var span = {
6833                 tag: 'span',
6834                 html : this.html || ''
6835             };
6836
6837             a.cn.push(span);
6838             
6839         }
6840         
6841         if (this.badge !== '') {
6842             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6843         }
6844         
6845         if (this.menu) {
6846             
6847             if(this.showArrow){
6848                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6849             }
6850             
6851             a.cls += ' dropdown-toggle treeview' ;
6852         }
6853         
6854         return cfg;
6855     },
6856     
6857     initEvents : function()
6858     { 
6859         if (typeof (this.menu) != 'undefined') {
6860             this.menu.parentType = this.xtype;
6861             this.menu.triggerEl = this.el;
6862             this.menu = this.addxtype(Roo.apply({}, this.menu));
6863         }
6864         
6865         this.el.on('click', this.onClick, this);
6866         
6867         if(this.badge !== ''){
6868             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6869         }
6870         
6871     },
6872     
6873     onClick : function(e)
6874     {
6875         if(this.disabled){
6876             e.preventDefault();
6877             return;
6878         }
6879         
6880         if(this.preventDefault){
6881             e.preventDefault();
6882         }
6883         
6884         this.fireEvent('click', this, e);
6885     },
6886     
6887     disable : function()
6888     {
6889         this.setDisabled(true);
6890     },
6891     
6892     enable : function()
6893     {
6894         this.setDisabled(false);
6895     },
6896     
6897     setDisabled : function(state)
6898     {
6899         if(this.disabled == state){
6900             return;
6901         }
6902         
6903         this.disabled = state;
6904         
6905         if (state) {
6906             this.el.addClass('disabled');
6907             return;
6908         }
6909         
6910         this.el.removeClass('disabled');
6911         
6912         return;
6913     },
6914     
6915     setActive : function(state)
6916     {
6917         if(this.active == state){
6918             return;
6919         }
6920         
6921         this.active = state;
6922         
6923         if (state) {
6924             this.el.addClass('active');
6925             return;
6926         }
6927         
6928         this.el.removeClass('active');
6929         
6930         return;
6931     },
6932     
6933     isActive: function () 
6934     {
6935         return this.active;
6936     },
6937     
6938     setBadge : function(str)
6939     {
6940         if(!this.badgeEl){
6941             return;
6942         }
6943         
6944         this.badgeEl.dom.innerHTML = str;
6945     }
6946     
6947    
6948      
6949  
6950 });
6951  
6952
6953  /*
6954  * - LGPL
6955  *
6956  * nav progress bar
6957  * 
6958  */
6959
6960 /**
6961  * @class Roo.bootstrap.nav.ProgressBar
6962  * @extends Roo.bootstrap.Component
6963  * @children Roo.bootstrap.nav.ProgressBarItem
6964  * Bootstrap NavProgressBar class
6965  * 
6966  * @constructor
6967  * Create a new nav progress bar - a bar indicating step along a process
6968  * @param {Object} config The config object
6969  */
6970
6971 Roo.bootstrap.nav.ProgressBar = function(config){
6972     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6973
6974     this.bullets = this.bullets || [];
6975    
6976 //    Roo.bootstrap.nav.ProgressBar.register(this);
6977      this.addEvents({
6978         /**
6979              * @event changed
6980              * Fires when the active item changes
6981              * @param {Roo.bootstrap.nav.ProgressBar} this
6982              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6983              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6984          */
6985         'changed': true
6986      });
6987     
6988 };
6989
6990 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6991     /**
6992      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6993      * Bullets for the Nav Progress bar for the toolbar
6994      */
6995     bullets : [],
6996     barItems : [],
6997     
6998     getAutoCreate : function()
6999     {
7000         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7001         
7002         cfg = {
7003             tag : 'div',
7004             cls : 'roo-navigation-bar-group',
7005             cn : [
7006                 {
7007                     tag : 'div',
7008                     cls : 'roo-navigation-top-bar'
7009                 },
7010                 {
7011                     tag : 'div',
7012                     cls : 'roo-navigation-bullets-bar',
7013                     cn : [
7014                         {
7015                             tag : 'ul',
7016                             cls : 'roo-navigation-bar'
7017                         }
7018                     ]
7019                 },
7020                 
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bottom-bar'
7024                 }
7025             ]
7026             
7027         };
7028         
7029         return cfg;
7030         
7031     },
7032     
7033     initEvents: function() 
7034     {
7035         
7036     },
7037     
7038     onRender : function(ct, position) 
7039     {
7040         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7041         
7042         if(this.bullets.length){
7043             Roo.each(this.bullets, function(b){
7044                this.addItem(b);
7045             }, this);
7046         }
7047         
7048         this.format();
7049         
7050     },
7051     
7052     addItem : function(cfg)
7053     {
7054         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7055         
7056         item.parentId = this.id;
7057         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7058         
7059         if(cfg.html){
7060             var top = new Roo.bootstrap.Element({
7061                 tag : 'div',
7062                 cls : 'roo-navigation-bar-text'
7063             });
7064             
7065             var bottom = new Roo.bootstrap.Element({
7066                 tag : 'div',
7067                 cls : 'roo-navigation-bar-text'
7068             });
7069             
7070             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7071             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7072             
7073             var topText = new Roo.bootstrap.Element({
7074                 tag : 'span',
7075                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7076             });
7077             
7078             var bottomText = new Roo.bootstrap.Element({
7079                 tag : 'span',
7080                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7081             });
7082             
7083             topText.onRender(top.el, null);
7084             bottomText.onRender(bottom.el, null);
7085             
7086             item.topEl = top;
7087             item.bottomEl = bottom;
7088         }
7089         
7090         this.barItems.push(item);
7091         
7092         return item;
7093     },
7094     
7095     getActive : function()
7096     {
7097         var active = false;
7098         
7099         Roo.each(this.barItems, function(v){
7100             
7101             if (!v.isActive()) {
7102                 return;
7103             }
7104             
7105             active = v;
7106             return false;
7107             
7108         });
7109         
7110         return active;
7111     },
7112     
7113     setActiveItem : function(item)
7114     {
7115         var prev = false;
7116         
7117         Roo.each(this.barItems, function(v){
7118             if (v.rid == item.rid) {
7119                 return ;
7120             }
7121             
7122             if (v.isActive()) {
7123                 v.setActive(false);
7124                 prev = v;
7125             }
7126         });
7127
7128         item.setActive(true);
7129         
7130         this.fireEvent('changed', this, item, prev);
7131     },
7132     
7133     getBarItem: function(rid)
7134     {
7135         var ret = false;
7136         
7137         Roo.each(this.barItems, function(e) {
7138             if (e.rid != rid) {
7139                 return;
7140             }
7141             
7142             ret =  e;
7143             return false;
7144         });
7145         
7146         return ret;
7147     },
7148     
7149     indexOfItem : function(item)
7150     {
7151         var index = false;
7152         
7153         Roo.each(this.barItems, function(v, i){
7154             
7155             if (v.rid != item.rid) {
7156                 return;
7157             }
7158             
7159             index = i;
7160             return false
7161         });
7162         
7163         return index;
7164     },
7165     
7166     setActiveNext : function()
7167     {
7168         var i = this.indexOfItem(this.getActive());
7169         
7170         if (i > this.barItems.length) {
7171             return;
7172         }
7173         
7174         this.setActiveItem(this.barItems[i+1]);
7175     },
7176     
7177     setActivePrev : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i  < 1) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i-1]);
7186     },
7187     
7188     format : function()
7189     {
7190         if(!this.barItems.length){
7191             return;
7192         }
7193      
7194         var width = 100 / this.barItems.length;
7195         
7196         Roo.each(this.barItems, function(i){
7197             i.el.setStyle('width', width + '%');
7198             i.topEl.el.setStyle('width', width + '%');
7199             i.bottomEl.el.setStyle('width', width + '%');
7200         }, this);
7201         
7202     }
7203     
7204 });
7205 /*
7206  * - LGPL
7207  *
7208  * Nav Progress Item
7209  * 
7210  */
7211
7212 /**
7213  * @class Roo.bootstrap.nav.ProgressBarItem
7214  * @extends Roo.bootstrap.Component
7215  * Bootstrap NavProgressBarItem class
7216  * @cfg {String} rid the reference id
7217  * @cfg {Boolean} active (true|false) Is item active default false
7218  * @cfg {Boolean} disabled (true|false) Is item active default false
7219  * @cfg {String} html
7220  * @cfg {String} position (top|bottom) text position default bottom
7221  * @cfg {String} icon show icon instead of number
7222  * 
7223  * @constructor
7224  * Create a new NavProgressBarItem
7225  * @param {Object} config The config object
7226  */
7227 Roo.bootstrap.nav.ProgressBarItem = function(config){
7228     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7229     this.addEvents({
7230         // raw events
7231         /**
7232          * @event click
7233          * The raw click event for the entire grid.
7234          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7235          * @param {Roo.EventObject} e
7236          */
7237         "click" : true
7238     });
7239    
7240 };
7241
7242 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7243     
7244     rid : '',
7245     active : false,
7246     disabled : false,
7247     html : '',
7248     position : 'bottom',
7249     icon : false,
7250     
7251     getAutoCreate : function()
7252     {
7253         var iconCls = 'roo-navigation-bar-item-icon';
7254         
7255         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7256         
7257         var cfg = {
7258             tag: 'li',
7259             cls: 'roo-navigation-bar-item',
7260             cn : [
7261                 {
7262                     tag : 'i',
7263                     cls : iconCls
7264                 }
7265             ]
7266         };
7267         
7268         if(this.active){
7269             cfg.cls += ' active';
7270         }
7271         if(this.disabled){
7272             cfg.cls += ' disabled';
7273         }
7274         
7275         return cfg;
7276     },
7277     
7278     disable : function()
7279     {
7280         this.setDisabled(true);
7281     },
7282     
7283     enable : function()
7284     {
7285         this.setDisabled(false);
7286     },
7287     
7288     initEvents: function() 
7289     {
7290         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7291         
7292         this.iconEl.on('click', this.onClick, this);
7293     },
7294     
7295     onClick : function(e)
7296     {
7297         e.preventDefault();
7298         
7299         if(this.disabled){
7300             return;
7301         }
7302         
7303         if(this.fireEvent('click', this, e) === false){
7304             return;
7305         };
7306         
7307         this.parent().setActiveItem(this);
7308     },
7309     
7310     isActive: function () 
7311     {
7312         return this.active;
7313     },
7314     
7315     setActive : function(state)
7316     {
7317         if(this.active == state){
7318             return;
7319         }
7320         
7321         this.active = state;
7322         
7323         if (state) {
7324             this.el.addClass('active');
7325             return;
7326         }
7327         
7328         this.el.removeClass('active');
7329         
7330         return;
7331     },
7332     
7333     setDisabled : function(state)
7334     {
7335         if(this.disabled == state){
7336             return;
7337         }
7338         
7339         this.disabled = state;
7340         
7341         if (state) {
7342             this.el.addClass('disabled');
7343             return;
7344         }
7345         
7346         this.el.removeClass('disabled');
7347     },
7348     
7349     tooltipEl : function()
7350     {
7351         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7352     }
7353 });
7354  
7355
7356  /*
7357  * - LGPL
7358  *
7359  *  Breadcrumb Nav
7360  * 
7361  */
7362 Roo.namespace('Roo.bootstrap.breadcrumb');
7363
7364
7365 /**
7366  * @class Roo.bootstrap.breadcrumb.Nav
7367  * @extends Roo.bootstrap.Component
7368  * Bootstrap Breadcrumb Nav Class
7369  *  
7370  * @children Roo.bootstrap.breadcrumb.Item
7371  * 
7372  * @constructor
7373  * Create a new breadcrumb.Nav
7374  * @param {Object} config The config object
7375  */
7376
7377
7378 Roo.bootstrap.breadcrumb.Nav = function(config){
7379     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7380     
7381     
7382 };
7383
7384 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7385     
7386     getAutoCreate : function()
7387     {
7388
7389         var cfg = {
7390             tag: 'nav',
7391             cn : [
7392                 {
7393                     tag : 'ol',
7394                     cls : 'breadcrumb'
7395                 }
7396             ]
7397             
7398         };
7399           
7400         return cfg;
7401     },
7402     
7403     initEvents: function()
7404     {
7405         this.olEl = this.el.select('ol',true).first();    
7406     },
7407     getChildContainer : function()
7408     {
7409         return this.olEl;  
7410     }
7411     
7412 });
7413
7414  /*
7415  * - LGPL
7416  *
7417  *  Breadcrumb Item
7418  * 
7419  */
7420
7421
7422 /**
7423  * @class Roo.bootstrap.breadcrumb.Nav
7424  * @extends Roo.bootstrap.Component
7425  * @children Roo.bootstrap.Component
7426  * @parent Roo.bootstrap.breadcrumb.Nav
7427  * Bootstrap Breadcrumb Nav Class
7428  *  
7429  * 
7430  * @cfg {String} html the content of the link.
7431  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7432  * @cfg {Boolean} active is it active
7433
7434  * 
7435  * @constructor
7436  * Create a new breadcrumb.Nav
7437  * @param {Object} config The config object
7438  */
7439
7440 Roo.bootstrap.breadcrumb.Item = function(config){
7441     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7442     this.addEvents({
7443         // img events
7444         /**
7445          * @event click
7446          * The img click event for the img.
7447          * @param {Roo.EventObject} e
7448          */
7449         "click" : true
7450     });
7451     
7452 };
7453
7454 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7455     
7456     href: false,
7457     html : '',
7458     
7459     getAutoCreate : function()
7460     {
7461
7462         var cfg = {
7463             tag: 'li',
7464             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7465         };
7466         if (this.href !== false) {
7467             cfg.cn = [{
7468                 tag : 'a',
7469                 href : this.href,
7470                 html : this.html
7471             }];
7472         } else {
7473             cfg.html = this.html;
7474         }
7475         
7476         return cfg;
7477     },
7478     
7479     initEvents: function()
7480     {
7481         if (this.href) {
7482             this.el.select('a', true).first().on('click',this.onClick, this)
7483         }
7484         
7485     },
7486     onClick : function(e)
7487     {
7488         e.preventDefault();
7489         this.fireEvent('click',this,  e);
7490     }
7491     
7492 });
7493
7494  /*
7495  * - LGPL
7496  *
7497  * row
7498  * 
7499  */
7500
7501 /**
7502  * @class Roo.bootstrap.Row
7503  * @extends Roo.bootstrap.Component
7504  * @children Roo.bootstrap.Component
7505  * Bootstrap Row class (contains columns...)
7506  * 
7507  * @constructor
7508  * Create a new Row
7509  * @param {Object} config The config object
7510  */
7511
7512 Roo.bootstrap.Row = function(config){
7513     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7514 };
7515
7516 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7517     
7518     getAutoCreate : function(){
7519        return {
7520             cls: 'row clearfix'
7521        };
7522     }
7523     
7524     
7525 });
7526
7527  
7528
7529  /*
7530  * - LGPL
7531  *
7532  * pagination
7533  * 
7534  */
7535
7536 /**
7537  * @class Roo.bootstrap.Pagination
7538  * @extends Roo.bootstrap.Component
7539  * @children Roo.bootstrap.Pagination
7540  * Bootstrap Pagination class
7541  * 
7542  * @cfg {String} size (xs|sm|md|lg|xl)
7543  * @cfg {Boolean} inverse 
7544  * 
7545  * @constructor
7546  * Create a new Pagination
7547  * @param {Object} config The config object
7548  */
7549
7550 Roo.bootstrap.Pagination = function(config){
7551     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7552 };
7553
7554 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7555     
7556     cls: false,
7557     size: false,
7558     inverse: false,
7559     
7560     getAutoCreate : function(){
7561         var cfg = {
7562             tag: 'ul',
7563                 cls: 'pagination'
7564         };
7565         if (this.inverse) {
7566             cfg.cls += ' inverse';
7567         }
7568         if (this.html) {
7569             cfg.html=this.html;
7570         }
7571         if (this.cls) {
7572             cfg.cls += " " + this.cls;
7573         }
7574         return cfg;
7575     }
7576    
7577 });
7578
7579  
7580
7581  /*
7582  * - LGPL
7583  *
7584  * Pagination item
7585  * 
7586  */
7587
7588
7589 /**
7590  * @class Roo.bootstrap.PaginationItem
7591  * @extends Roo.bootstrap.Component
7592  * Bootstrap PaginationItem class
7593  * @cfg {String} html text
7594  * @cfg {String} href the link
7595  * @cfg {Boolean} preventDefault (true | false) default true
7596  * @cfg {Boolean} active (true | false) default false
7597  * @cfg {Boolean} disabled default false
7598  * 
7599  * 
7600  * @constructor
7601  * Create a new PaginationItem
7602  * @param {Object} config The config object
7603  */
7604
7605
7606 Roo.bootstrap.PaginationItem = function(config){
7607     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7608     this.addEvents({
7609         // raw events
7610         /**
7611          * @event click
7612          * The raw click event for the entire grid.
7613          * @param {Roo.EventObject} e
7614          */
7615         "click" : true
7616     });
7617 };
7618
7619 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7620     
7621     href : false,
7622     html : false,
7623     preventDefault: true,
7624     active : false,
7625     cls : false,
7626     disabled: false,
7627     
7628     getAutoCreate : function(){
7629         var cfg= {
7630             tag: 'li',
7631             cn: [
7632                 {
7633                     tag : 'a',
7634                     href : this.href ? this.href : '#',
7635                     html : this.html ? this.html : ''
7636                 }
7637             ]
7638         };
7639         
7640         if(this.cls){
7641             cfg.cls = this.cls;
7642         }
7643         
7644         if(this.disabled){
7645             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7646         }
7647         
7648         if(this.active){
7649             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7650         }
7651         
7652         return cfg;
7653     },
7654     
7655     initEvents: function() {
7656         
7657         this.el.on('click', this.onClick, this);
7658         
7659     },
7660     onClick : function(e)
7661     {
7662         Roo.log('PaginationItem on click ');
7663         if(this.preventDefault){
7664             e.preventDefault();
7665         }
7666         
7667         if(this.disabled){
7668             return;
7669         }
7670         
7671         this.fireEvent('click', this, e);
7672     }
7673    
7674 });
7675
7676  
7677
7678  /*
7679  * - LGPL
7680  *
7681  * slider
7682  * 
7683  */
7684
7685
7686 /**
7687  * @class Roo.bootstrap.Slider
7688  * @extends Roo.bootstrap.Component
7689  * Bootstrap Slider class
7690  *    
7691  * @constructor
7692  * Create a new Slider
7693  * @param {Object} config The config object
7694  */
7695
7696 Roo.bootstrap.Slider = function(config){
7697     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7698 };
7699
7700 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7701     
7702     getAutoCreate : function(){
7703         
7704         var cfg = {
7705             tag: 'div',
7706             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7707             cn: [
7708                 {
7709                     tag: 'a',
7710                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7711                 }
7712             ]
7713         };
7714         
7715         return cfg;
7716     }
7717    
7718 });
7719
7720  /*
7721  * Based on:
7722  * Ext JS Library 1.1.1
7723  * Copyright(c) 2006-2007, Ext JS, LLC.
7724  *
7725  * Originally Released Under LGPL - original licence link has changed is not relivant.
7726  *
7727  * Fork - LGPL
7728  * <script type="text/javascript">
7729  */
7730  /**
7731  * @extends Roo.dd.DDProxy
7732  * @class Roo.grid.SplitDragZone
7733  * Support for Column Header resizing
7734  * @constructor
7735  * @param {Object} config
7736  */
7737 // private
7738 // This is a support class used internally by the Grid components
7739 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7740     this.grid = grid;
7741     this.view = grid.getView();
7742     this.proxy = this.view.resizeProxy;
7743     Roo.grid.SplitDragZone.superclass.constructor.call(
7744         this,
7745         hd, // ID
7746         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7747         {  // CONFIG
7748             dragElId : Roo.id(this.proxy.dom),
7749             resizeFrame:false
7750         }
7751     );
7752     
7753     this.setHandleElId(Roo.id(hd));
7754     if (hd2 !== false) {
7755         this.setOuterHandleElId(Roo.id(hd2));
7756     }
7757     
7758     this.scroll = false;
7759 };
7760 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7761     fly: Roo.Element.fly,
7762
7763     b4StartDrag : function(x, y){
7764         this.view.headersDisabled = true;
7765         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7766                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7767         );
7768         this.proxy.setHeight(h);
7769         
7770         // for old system colWidth really stored the actual width?
7771         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7772         // which in reality did not work.. - it worked only for fixed sizes
7773         // for resizable we need to use actual sizes.
7774         var w = this.cm.getColumnWidth(this.cellIndex);
7775         if (!this.view.mainWrap) {
7776             // bootstrap.
7777             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7778         }
7779         
7780         
7781         
7782         // this was w-this.grid.minColumnWidth;
7783         // doesnt really make sense? - w = thie curren width or the rendered one?
7784         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7785         this.resetConstraints();
7786         this.setXConstraint(minw, 1000);
7787         this.setYConstraint(0, 0);
7788         this.minX = x - minw;
7789         this.maxX = x + 1000;
7790         this.startPos = x;
7791         if (!this.view.mainWrap) { // this is Bootstrap code..
7792             this.getDragEl().style.display='block';
7793         }
7794         
7795         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7796     },
7797
7798
7799     handleMouseDown : function(e){
7800         ev = Roo.EventObject.setEvent(e);
7801         var t = this.fly(ev.getTarget());
7802         if(t.hasClass("x-grid-split")){
7803             this.cellIndex = this.view.getCellIndex(t.dom);
7804             this.split = t.dom;
7805             this.cm = this.grid.colModel;
7806             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7807                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7808             }
7809         }
7810     },
7811
7812     endDrag : function(e){
7813         this.view.headersDisabled = false;
7814         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7815         var diff = endX - this.startPos;
7816         // 
7817         var w = this.cm.getColumnWidth(this.cellIndex);
7818         if (!this.view.mainWrap) {
7819             w = 0;
7820         }
7821         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7822     },
7823
7824     autoOffset : function(){
7825         this.setDelta(0,0);
7826     }
7827 });/*
7828  * Based on:
7829  * Ext JS Library 1.1.1
7830  * Copyright(c) 2006-2007, Ext JS, LLC.
7831  *
7832  * Originally Released Under LGPL - original licence link has changed is not relivant.
7833  *
7834  * Fork - LGPL
7835  * <script type="text/javascript">
7836  */
7837
7838 /**
7839  * @class Roo.grid.AbstractSelectionModel
7840  * @extends Roo.util.Observable
7841  * @abstract
7842  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7843  * implemented by descendant classes.  This class should not be directly instantiated.
7844  * @constructor
7845  */
7846 Roo.grid.AbstractSelectionModel = function(){
7847     this.locked = false;
7848     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7849 };
7850
7851 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7852     /** @ignore Called by the grid automatically. Do not call directly. */
7853     init : function(grid){
7854         this.grid = grid;
7855         this.initEvents();
7856     },
7857
7858     /**
7859      * Locks the selections.
7860      */
7861     lock : function(){
7862         this.locked = true;
7863     },
7864
7865     /**
7866      * Unlocks the selections.
7867      */
7868     unlock : function(){
7869         this.locked = false;
7870     },
7871
7872     /**
7873      * Returns true if the selections are locked.
7874      * @return {Boolean}
7875      */
7876     isLocked : function(){
7877         return this.locked;
7878     }
7879 });/*
7880  * Based on:
7881  * Ext JS Library 1.1.1
7882  * Copyright(c) 2006-2007, Ext JS, LLC.
7883  *
7884  * Originally Released Under LGPL - original licence link has changed is not relivant.
7885  *
7886  * Fork - LGPL
7887  * <script type="text/javascript">
7888  */
7889 /**
7890  * @extends Roo.grid.AbstractSelectionModel
7891  * @class Roo.grid.RowSelectionModel
7892  * The default SelectionModel used by {@link Roo.grid.Grid}.
7893  * It supports multiple selections and keyboard selection/navigation. 
7894  * @constructor
7895  * @param {Object} config
7896  */
7897 Roo.grid.RowSelectionModel = function(config){
7898     Roo.apply(this, config);
7899     this.selections = new Roo.util.MixedCollection(false, function(o){
7900         return o.id;
7901     });
7902
7903     this.last = false;
7904     this.lastActive = false;
7905
7906     this.addEvents({
7907         /**
7908         * @event selectionchange
7909         * Fires when the selection changes
7910         * @param {SelectionModel} this
7911         */
7912        "selectionchange" : true,
7913        /**
7914         * @event afterselectionchange
7915         * Fires after the selection changes (eg. by key press or clicking)
7916         * @param {SelectionModel} this
7917         */
7918        "afterselectionchange" : true,
7919        /**
7920         * @event beforerowselect
7921         * Fires when a row is selected being selected, return false to cancel.
7922         * @param {SelectionModel} this
7923         * @param {Number} rowIndex The selected index
7924         * @param {Boolean} keepExisting False if other selections will be cleared
7925         */
7926        "beforerowselect" : true,
7927        /**
7928         * @event rowselect
7929         * Fires when a row is selected.
7930         * @param {SelectionModel} this
7931         * @param {Number} rowIndex The selected index
7932         * @param {Roo.data.Record} r The record
7933         */
7934        "rowselect" : true,
7935        /**
7936         * @event rowdeselect
7937         * Fires when a row is deselected.
7938         * @param {SelectionModel} this
7939         * @param {Number} rowIndex The selected index
7940         */
7941         "rowdeselect" : true
7942     });
7943     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7944     this.locked = false;
7945 };
7946
7947 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7948     /**
7949      * @cfg {Boolean} singleSelect
7950      * True to allow selection of only one row at a time (defaults to false)
7951      */
7952     singleSelect : false,
7953
7954     // private
7955     initEvents : function(){
7956
7957         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7958             this.grid.on("mousedown", this.handleMouseDown, this);
7959         }else{ // allow click to work like normal
7960             this.grid.on("rowclick", this.handleDragableRowClick, this);
7961         }
7962         // bootstrap does not have a view..
7963         var view = this.grid.view ? this.grid.view : this.grid;
7964         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7965             "up" : function(e){
7966                 if(!e.shiftKey){
7967                     this.selectPrevious(e.shiftKey);
7968                 }else if(this.last !== false && this.lastActive !== false){
7969                     var last = this.last;
7970                     this.selectRange(this.last,  this.lastActive-1);
7971                     view.focusRow(this.lastActive);
7972                     if(last !== false){
7973                         this.last = last;
7974                     }
7975                 }else{
7976                     this.selectFirstRow();
7977                 }
7978                 this.fireEvent("afterselectionchange", this);
7979             },
7980             "down" : function(e){
7981                 if(!e.shiftKey){
7982                     this.selectNext(e.shiftKey);
7983                 }else if(this.last !== false && this.lastActive !== false){
7984                     var last = this.last;
7985                     this.selectRange(this.last,  this.lastActive+1);
7986                     view.focusRow(this.lastActive);
7987                     if(last !== false){
7988                         this.last = last;
7989                     }
7990                 }else{
7991                     this.selectFirstRow();
7992                 }
7993                 this.fireEvent("afterselectionchange", this);
7994             },
7995             scope: this
7996         });
7997
7998          
7999         view.on("refresh", this.onRefresh, this);
8000         view.on("rowupdated", this.onRowUpdated, this);
8001         view.on("rowremoved", this.onRemove, this);
8002     },
8003
8004     // private
8005     onRefresh : function(){
8006         var ds = this.grid.ds, i, v = this.grid.view;
8007         var s = this.selections;
8008         s.each(function(r){
8009             if((i = ds.indexOfId(r.id)) != -1){
8010                 v.onRowSelect(i);
8011                 s.add(ds.getAt(i)); // updating the selection relate data
8012             }else{
8013                 s.remove(r);
8014             }
8015         });
8016     },
8017
8018     // private
8019     onRemove : function(v, index, r){
8020         this.selections.remove(r);
8021     },
8022
8023     // private
8024     onRowUpdated : function(v, index, r){
8025         if(this.isSelected(r)){
8026             v.onRowSelect(index);
8027         }
8028     },
8029
8030     /**
8031      * Select records.
8032      * @param {Array} records The records to select
8033      * @param {Boolean} keepExisting (optional) True to keep existing selections
8034      */
8035     selectRecords : function(records, keepExisting){
8036         if(!keepExisting){
8037             this.clearSelections();
8038         }
8039         var ds = this.grid.ds;
8040         for(var i = 0, len = records.length; i < len; i++){
8041             this.selectRow(ds.indexOf(records[i]), true);
8042         }
8043     },
8044
8045     /**
8046      * Gets the number of selected rows.
8047      * @return {Number}
8048      */
8049     getCount : function(){
8050         return this.selections.length;
8051     },
8052
8053     /**
8054      * Selects the first row in the grid.
8055      */
8056     selectFirstRow : function(){
8057         this.selectRow(0);
8058     },
8059
8060     /**
8061      * Select the last row.
8062      * @param {Boolean} keepExisting (optional) True to keep existing selections
8063      */
8064     selectLastRow : function(keepExisting){
8065         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8066     },
8067
8068     /**
8069      * Selects the row immediately following the last selected row.
8070      * @param {Boolean} keepExisting (optional) True to keep existing selections
8071      */
8072     selectNext : function(keepExisting){
8073         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8074             this.selectRow(this.last+1, keepExisting);
8075             var view = this.grid.view ? this.grid.view : this.grid;
8076             view.focusRow(this.last);
8077         }
8078     },
8079
8080     /**
8081      * Selects the row that precedes the last selected row.
8082      * @param {Boolean} keepExisting (optional) True to keep existing selections
8083      */
8084     selectPrevious : function(keepExisting){
8085         if(this.last){
8086             this.selectRow(this.last-1, keepExisting);
8087             var view = this.grid.view ? this.grid.view : this.grid;
8088             view.focusRow(this.last);
8089         }
8090     },
8091
8092     /**
8093      * Returns the selected records
8094      * @return {Array} Array of selected records
8095      */
8096     getSelections : function(){
8097         return [].concat(this.selections.items);
8098     },
8099
8100     /**
8101      * Returns the first selected record.
8102      * @return {Record}
8103      */
8104     getSelected : function(){
8105         return this.selections.itemAt(0);
8106     },
8107
8108
8109     /**
8110      * Clears all selections.
8111      */
8112     clearSelections : function(fast){
8113         if(this.locked) {
8114             return;
8115         }
8116         if(fast !== true){
8117             var ds = this.grid.ds;
8118             var s = this.selections;
8119             s.each(function(r){
8120                 this.deselectRow(ds.indexOfId(r.id));
8121             }, this);
8122             s.clear();
8123         }else{
8124             this.selections.clear();
8125         }
8126         this.last = false;
8127     },
8128
8129
8130     /**
8131      * Selects all rows.
8132      */
8133     selectAll : function(){
8134         if(this.locked) {
8135             return;
8136         }
8137         this.selections.clear();
8138         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8139             this.selectRow(i, true);
8140         }
8141     },
8142
8143     /**
8144      * Returns True if there is a selection.
8145      * @return {Boolean}
8146      */
8147     hasSelection : function(){
8148         return this.selections.length > 0;
8149     },
8150
8151     /**
8152      * Returns True if the specified row is selected.
8153      * @param {Number/Record} record The record or index of the record to check
8154      * @return {Boolean}
8155      */
8156     isSelected : function(index){
8157         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8158         return (r && this.selections.key(r.id) ? true : false);
8159     },
8160
8161     /**
8162      * Returns True if the specified record id is selected.
8163      * @param {String} id The id of record to check
8164      * @return {Boolean}
8165      */
8166     isIdSelected : function(id){
8167         return (this.selections.key(id) ? true : false);
8168     },
8169
8170     // private
8171     handleMouseDown : function(e, t)
8172     {
8173         var view = this.grid.view ? this.grid.view : this.grid;
8174         var rowIndex;
8175         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8176             return;
8177         };
8178         if(e.shiftKey && this.last !== false){
8179             var last = this.last;
8180             this.selectRange(last, rowIndex, e.ctrlKey);
8181             this.last = last; // reset the last
8182             view.focusRow(rowIndex);
8183         }else{
8184             var isSelected = this.isSelected(rowIndex);
8185             if(e.button !== 0 && isSelected){
8186                 view.focusRow(rowIndex);
8187             }else if(e.ctrlKey && isSelected){
8188                 this.deselectRow(rowIndex);
8189             }else if(!isSelected){
8190                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8191                 view.focusRow(rowIndex);
8192             }
8193         }
8194         this.fireEvent("afterselectionchange", this);
8195     },
8196     // private
8197     handleDragableRowClick :  function(grid, rowIndex, e) 
8198     {
8199         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8200             this.selectRow(rowIndex, false);
8201             var view = this.grid.view ? this.grid.view : this.grid;
8202             view.focusRow(rowIndex);
8203              this.fireEvent("afterselectionchange", this);
8204         }
8205     },
8206     
8207     /**
8208      * Selects multiple rows.
8209      * @param {Array} rows Array of the indexes of the row to select
8210      * @param {Boolean} keepExisting (optional) True to keep existing selections
8211      */
8212     selectRows : function(rows, keepExisting){
8213         if(!keepExisting){
8214             this.clearSelections();
8215         }
8216         for(var i = 0, len = rows.length; i < len; i++){
8217             this.selectRow(rows[i], true);
8218         }
8219     },
8220
8221     /**
8222      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8223      * @param {Number} startRow The index of the first row in the range
8224      * @param {Number} endRow The index of the last row in the range
8225      * @param {Boolean} keepExisting (optional) True to retain existing selections
8226      */
8227     selectRange : function(startRow, endRow, keepExisting){
8228         if(this.locked) {
8229             return;
8230         }
8231         if(!keepExisting){
8232             this.clearSelections();
8233         }
8234         if(startRow <= endRow){
8235             for(var i = startRow; i <= endRow; i++){
8236                 this.selectRow(i, true);
8237             }
8238         }else{
8239             for(var i = startRow; i >= endRow; i--){
8240                 this.selectRow(i, true);
8241             }
8242         }
8243     },
8244
8245     /**
8246      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8247      * @param {Number} startRow The index of the first row in the range
8248      * @param {Number} endRow The index of the last row in the range
8249      */
8250     deselectRange : function(startRow, endRow, preventViewNotify){
8251         if(this.locked) {
8252             return;
8253         }
8254         for(var i = startRow; i <= endRow; i++){
8255             this.deselectRow(i, preventViewNotify);
8256         }
8257     },
8258
8259     /**
8260      * Selects a row.
8261      * @param {Number} row The index of the row to select
8262      * @param {Boolean} keepExisting (optional) True to keep existing selections
8263      */
8264     selectRow : function(index, keepExisting, preventViewNotify){
8265         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8266             return;
8267         }
8268         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8269             if(!keepExisting || this.singleSelect){
8270                 this.clearSelections();
8271             }
8272             var r = this.grid.ds.getAt(index);
8273             this.selections.add(r);
8274             this.last = this.lastActive = index;
8275             if(!preventViewNotify){
8276                 var view = this.grid.view ? this.grid.view : this.grid;
8277                 view.onRowSelect(index);
8278             }
8279             this.fireEvent("rowselect", this, index, r);
8280             this.fireEvent("selectionchange", this);
8281         }
8282     },
8283
8284     /**
8285      * Deselects a row.
8286      * @param {Number} row The index of the row to deselect
8287      */
8288     deselectRow : function(index, preventViewNotify){
8289         if(this.locked) {
8290             return;
8291         }
8292         if(this.last == index){
8293             this.last = false;
8294         }
8295         if(this.lastActive == index){
8296             this.lastActive = false;
8297         }
8298         var r = this.grid.ds.getAt(index);
8299         this.selections.remove(r);
8300         if(!preventViewNotify){
8301             var view = this.grid.view ? this.grid.view : this.grid;
8302             view.onRowDeselect(index);
8303         }
8304         this.fireEvent("rowdeselect", this, index);
8305         this.fireEvent("selectionchange", this);
8306     },
8307
8308     // private
8309     restoreLast : function(){
8310         if(this._last){
8311             this.last = this._last;
8312         }
8313     },
8314
8315     // private
8316     acceptsNav : function(row, col, cm){
8317         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8318     },
8319
8320     // private
8321     onEditorKey : function(field, e){
8322         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8323         if(k == e.TAB){
8324             e.stopEvent();
8325             ed.completeEdit();
8326             if(e.shiftKey){
8327                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8328             }else{
8329                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8330             }
8331         }else if(k == e.ENTER && !e.ctrlKey){
8332             e.stopEvent();
8333             ed.completeEdit();
8334             if(e.shiftKey){
8335                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8336             }else{
8337                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8338             }
8339         }else if(k == e.ESC){
8340             ed.cancelEdit();
8341         }
8342         if(newCell){
8343             g.startEditing(newCell[0], newCell[1]);
8344         }
8345     }
8346 });/*
8347  * Based on:
8348  * Ext JS Library 1.1.1
8349  * Copyright(c) 2006-2007, Ext JS, LLC.
8350  *
8351  * Originally Released Under LGPL - original licence link has changed is not relivant.
8352  *
8353  * Fork - LGPL
8354  * <script type="text/javascript">
8355  */
8356  
8357
8358 /**
8359  * @class Roo.grid.ColumnModel
8360  * @extends Roo.util.Observable
8361  * This is the default implementation of a ColumnModel used by the Grid. It defines
8362  * the columns in the grid.
8363  * <br>Usage:<br>
8364  <pre><code>
8365  var colModel = new Roo.grid.ColumnModel([
8366         {header: "Ticker", width: 60, sortable: true, locked: true},
8367         {header: "Company Name", width: 150, sortable: true},
8368         {header: "Market Cap.", width: 100, sortable: true},
8369         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8370         {header: "Employees", width: 100, sortable: true, resizable: false}
8371  ]);
8372  </code></pre>
8373  * <p>
8374  
8375  * The config options listed for this class are options which may appear in each
8376  * individual column definition.
8377  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8378  * @constructor
8379  * @param {Object} config An Array of column config objects. See this class's
8380  * config objects for details.
8381 */
8382 Roo.grid.ColumnModel = function(config){
8383         /**
8384      * The config passed into the constructor
8385      */
8386     this.config = []; //config;
8387     this.lookup = {};
8388
8389     // if no id, create one
8390     // if the column does not have a dataIndex mapping,
8391     // map it to the order it is in the config
8392     for(var i = 0, len = config.length; i < len; i++){
8393         this.addColumn(config[i]);
8394         
8395     }
8396
8397     /**
8398      * The width of columns which have no width specified (defaults to 100)
8399      * @type Number
8400      */
8401     this.defaultWidth = 100;
8402
8403     /**
8404      * Default sortable of columns which have no sortable specified (defaults to false)
8405      * @type Boolean
8406      */
8407     this.defaultSortable = false;
8408
8409     this.addEvents({
8410         /**
8411              * @event widthchange
8412              * Fires when the width of a column changes.
8413              * @param {ColumnModel} this
8414              * @param {Number} columnIndex The column index
8415              * @param {Number} newWidth The new width
8416              */
8417             "widthchange": true,
8418         /**
8419              * @event headerchange
8420              * Fires when the text of a header changes.
8421              * @param {ColumnModel} this
8422              * @param {Number} columnIndex The column index
8423              * @param {Number} newText The new header text
8424              */
8425             "headerchange": true,
8426         /**
8427              * @event hiddenchange
8428              * Fires when a column is hidden or "unhidden".
8429              * @param {ColumnModel} this
8430              * @param {Number} columnIndex The column index
8431              * @param {Boolean} hidden true if hidden, false otherwise
8432              */
8433             "hiddenchange": true,
8434             /**
8435          * @event columnmoved
8436          * Fires when a column is moved.
8437          * @param {ColumnModel} this
8438          * @param {Number} oldIndex
8439          * @param {Number} newIndex
8440          */
8441         "columnmoved" : true,
8442         /**
8443          * @event columlockchange
8444          * Fires when a column's locked state is changed
8445          * @param {ColumnModel} this
8446          * @param {Number} colIndex
8447          * @param {Boolean} locked true if locked
8448          */
8449         "columnlockchange" : true
8450     });
8451     Roo.grid.ColumnModel.superclass.constructor.call(this);
8452 };
8453 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8454     /**
8455      * @cfg {String} header The header text to display in the Grid view.
8456      */
8457         /**
8458      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8459      */
8460         /**
8461      * @cfg {String} smHeader Header at Bootsrap Small width
8462      */
8463         /**
8464      * @cfg {String} mdHeader Header at Bootsrap Medium width
8465      */
8466         /**
8467      * @cfg {String} lgHeader Header at Bootsrap Large width
8468      */
8469         /**
8470      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8471      */
8472     /**
8473      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8474      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8475      * specified, the column's index is used as an index into the Record's data Array.
8476      */
8477     /**
8478      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8479      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8480      */
8481     /**
8482      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8483      * Defaults to the value of the {@link #defaultSortable} property.
8484      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8485      */
8486     /**
8487      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8488      */
8489     /**
8490      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8491      */
8492     /**
8493      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8494      */
8495     /**
8496      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8497      */
8498     /**
8499      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8500      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8501      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8502      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8503      */
8504        /**
8505      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8506      */
8507     /**
8508      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8509      */
8510     /**
8511      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8512      */
8513     /**
8514      * @cfg {String} cursor (Optional)
8515      */
8516     /**
8517      * @cfg {String} tooltip (Optional)
8518      */
8519     /**
8520      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8521      */
8522     /**
8523      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8524      */
8525     /**
8526      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8527      */
8528     /**
8529      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8530      */
8531         /**
8532      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8533      */
8534     /**
8535      * Returns the id of the column at the specified index.
8536      * @param {Number} index The column index
8537      * @return {String} the id
8538      */
8539     getColumnId : function(index){
8540         return this.config[index].id;
8541     },
8542
8543     /**
8544      * Returns the column for a specified id.
8545      * @param {String} id The column id
8546      * @return {Object} the column
8547      */
8548     getColumnById : function(id){
8549         return this.lookup[id];
8550     },
8551
8552     
8553     /**
8554      * Returns the column Object for a specified dataIndex.
8555      * @param {String} dataIndex The column dataIndex
8556      * @return {Object|Boolean} the column or false if not found
8557      */
8558     getColumnByDataIndex: function(dataIndex){
8559         var index = this.findColumnIndex(dataIndex);
8560         return index > -1 ? this.config[index] : false;
8561     },
8562     
8563     /**
8564      * Returns the index for a specified column id.
8565      * @param {String} id The column id
8566      * @return {Number} the index, or -1 if not found
8567      */
8568     getIndexById : function(id){
8569         for(var i = 0, len = this.config.length; i < len; i++){
8570             if(this.config[i].id == id){
8571                 return i;
8572             }
8573         }
8574         return -1;
8575     },
8576     
8577     /**
8578      * Returns the index for a specified column dataIndex.
8579      * @param {String} dataIndex The column dataIndex
8580      * @return {Number} the index, or -1 if not found
8581      */
8582     
8583     findColumnIndex : function(dataIndex){
8584         for(var i = 0, len = this.config.length; i < len; i++){
8585             if(this.config[i].dataIndex == dataIndex){
8586                 return i;
8587             }
8588         }
8589         return -1;
8590     },
8591     
8592     
8593     moveColumn : function(oldIndex, newIndex){
8594         var c = this.config[oldIndex];
8595         this.config.splice(oldIndex, 1);
8596         this.config.splice(newIndex, 0, c);
8597         this.dataMap = null;
8598         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8599     },
8600
8601     isLocked : function(colIndex){
8602         return this.config[colIndex].locked === true;
8603     },
8604
8605     setLocked : function(colIndex, value, suppressEvent){
8606         if(this.isLocked(colIndex) == value){
8607             return;
8608         }
8609         this.config[colIndex].locked = value;
8610         if(!suppressEvent){
8611             this.fireEvent("columnlockchange", this, colIndex, value);
8612         }
8613     },
8614
8615     getTotalLockedWidth : function(){
8616         var totalWidth = 0;
8617         for(var i = 0; i < this.config.length; i++){
8618             if(this.isLocked(i) && !this.isHidden(i)){
8619                 this.totalWidth += this.getColumnWidth(i);
8620             }
8621         }
8622         return totalWidth;
8623     },
8624
8625     getLockedCount : function(){
8626         for(var i = 0, len = this.config.length; i < len; i++){
8627             if(!this.isLocked(i)){
8628                 return i;
8629             }
8630         }
8631         
8632         return this.config.length;
8633     },
8634
8635     /**
8636      * Returns the number of columns.
8637      * @return {Number}
8638      */
8639     getColumnCount : function(visibleOnly){
8640         if(visibleOnly === true){
8641             var c = 0;
8642             for(var i = 0, len = this.config.length; i < len; i++){
8643                 if(!this.isHidden(i)){
8644                     c++;
8645                 }
8646             }
8647             return c;
8648         }
8649         return this.config.length;
8650     },
8651
8652     /**
8653      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8654      * @param {Function} fn
8655      * @param {Object} scope (optional)
8656      * @return {Array} result
8657      */
8658     getColumnsBy : function(fn, scope){
8659         var r = [];
8660         for(var i = 0, len = this.config.length; i < len; i++){
8661             var c = this.config[i];
8662             if(fn.call(scope||this, c, i) === true){
8663                 r[r.length] = c;
8664             }
8665         }
8666         return r;
8667     },
8668
8669     /**
8670      * Returns true if the specified column is sortable.
8671      * @param {Number} col The column index
8672      * @return {Boolean}
8673      */
8674     isSortable : function(col){
8675         if(typeof this.config[col].sortable == "undefined"){
8676             return this.defaultSortable;
8677         }
8678         return this.config[col].sortable;
8679     },
8680
8681     /**
8682      * Returns the rendering (formatting) function defined for the column.
8683      * @param {Number} col The column index.
8684      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8685      */
8686     getRenderer : function(col){
8687         if(!this.config[col].renderer){
8688             return Roo.grid.ColumnModel.defaultRenderer;
8689         }
8690         return this.config[col].renderer;
8691     },
8692
8693     /**
8694      * Sets the rendering (formatting) function for a column.
8695      * @param {Number} col The column index
8696      * @param {Function} fn The function to use to process the cell's raw data
8697      * to return HTML markup for the grid view. The render function is called with
8698      * the following parameters:<ul>
8699      * <li>Data value.</li>
8700      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8701      * <li>css A CSS style string to apply to the table cell.</li>
8702      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8703      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8704      * <li>Row index</li>
8705      * <li>Column index</li>
8706      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8707      */
8708     setRenderer : function(col, fn){
8709         this.config[col].renderer = fn;
8710     },
8711
8712     /**
8713      * Returns the width for the specified column.
8714      * @param {Number} col The column index
8715      * @param (optional) {String} gridSize bootstrap width size.
8716      * @return {Number}
8717      */
8718     getColumnWidth : function(col, gridSize)
8719         {
8720                 var cfg = this.config[col];
8721                 
8722                 if (typeof(gridSize) == 'undefined') {
8723                         return cfg.width * 1 || this.defaultWidth;
8724                 }
8725                 if (gridSize === false) { // if we set it..
8726                         return cfg.width || false;
8727                 }
8728                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8729                 
8730                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8731                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8732                                 continue;
8733                         }
8734                         return cfg[ sizes[i] ];
8735                 }
8736                 return 1;
8737                 
8738     },
8739
8740     /**
8741      * Sets the width for a column.
8742      * @param {Number} col The column index
8743      * @param {Number} width The new width
8744      */
8745     setColumnWidth : function(col, width, suppressEvent){
8746         this.config[col].width = width;
8747         this.totalWidth = null;
8748         if(!suppressEvent){
8749              this.fireEvent("widthchange", this, col, width);
8750         }
8751     },
8752
8753     /**
8754      * Returns the total width of all columns.
8755      * @param {Boolean} includeHidden True to include hidden column widths
8756      * @return {Number}
8757      */
8758     getTotalWidth : function(includeHidden){
8759         if(!this.totalWidth){
8760             this.totalWidth = 0;
8761             for(var i = 0, len = this.config.length; i < len; i++){
8762                 if(includeHidden || !this.isHidden(i)){
8763                     this.totalWidth += this.getColumnWidth(i);
8764                 }
8765             }
8766         }
8767         return this.totalWidth;
8768     },
8769
8770     /**
8771      * Returns the header for the specified column.
8772      * @param {Number} col The column index
8773      * @return {String}
8774      */
8775     getColumnHeader : function(col){
8776         return this.config[col].header;
8777     },
8778
8779     /**
8780      * Sets the header for a column.
8781      * @param {Number} col The column index
8782      * @param {String} header The new header
8783      */
8784     setColumnHeader : function(col, header){
8785         this.config[col].header = header;
8786         this.fireEvent("headerchange", this, col, header);
8787     },
8788
8789     /**
8790      * Returns the tooltip for the specified column.
8791      * @param {Number} col The column index
8792      * @return {String}
8793      */
8794     getColumnTooltip : function(col){
8795             return this.config[col].tooltip;
8796     },
8797     /**
8798      * Sets the tooltip for a column.
8799      * @param {Number} col The column index
8800      * @param {String} tooltip The new tooltip
8801      */
8802     setColumnTooltip : function(col, tooltip){
8803             this.config[col].tooltip = tooltip;
8804     },
8805
8806     /**
8807      * Returns the dataIndex for the specified column.
8808      * @param {Number} col The column index
8809      * @return {Number}
8810      */
8811     getDataIndex : function(col){
8812         return this.config[col].dataIndex;
8813     },
8814
8815     /**
8816      * Sets the dataIndex for a column.
8817      * @param {Number} col The column index
8818      * @param {Number} dataIndex The new dataIndex
8819      */
8820     setDataIndex : function(col, dataIndex){
8821         this.config[col].dataIndex = dataIndex;
8822     },
8823
8824     
8825     
8826     /**
8827      * Returns true if the cell is editable.
8828      * @param {Number} colIndex The column index
8829      * @param {Number} rowIndex The row index - this is nto actually used..?
8830      * @return {Boolean}
8831      */
8832     isCellEditable : function(colIndex, rowIndex){
8833         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8834     },
8835
8836     /**
8837      * Returns the editor defined for the cell/column.
8838      * return false or null to disable editing.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index
8841      * @return {Object}
8842      */
8843     getCellEditor : function(colIndex, rowIndex){
8844         return this.config[colIndex].editor;
8845     },
8846
8847     /**
8848      * Sets if a column is editable.
8849      * @param {Number} col The column index
8850      * @param {Boolean} editable True if the column is editable
8851      */
8852     setEditable : function(col, editable){
8853         this.config[col].editable = editable;
8854     },
8855
8856
8857     /**
8858      * Returns true if the column is hidden.
8859      * @param {Number} colIndex The column index
8860      * @return {Boolean}
8861      */
8862     isHidden : function(colIndex){
8863         return this.config[colIndex].hidden;
8864     },
8865
8866
8867     /**
8868      * Returns true if the column width cannot be changed
8869      */
8870     isFixed : function(colIndex){
8871         return this.config[colIndex].fixed;
8872     },
8873
8874     /**
8875      * Returns true if the column can be resized
8876      * @return {Boolean}
8877      */
8878     isResizable : function(colIndex){
8879         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8880     },
8881     /**
8882      * Sets if a column is hidden.
8883      * @param {Number} colIndex The column index
8884      * @param {Boolean} hidden True if the column is hidden
8885      */
8886     setHidden : function(colIndex, hidden){
8887         this.config[colIndex].hidden = hidden;
8888         this.totalWidth = null;
8889         this.fireEvent("hiddenchange", this, colIndex, hidden);
8890     },
8891
8892     /**
8893      * Sets the editor for a column.
8894      * @param {Number} col The column index
8895      * @param {Object} editor The editor object
8896      */
8897     setEditor : function(col, editor){
8898         this.config[col].editor = editor;
8899     },
8900     /**
8901      * Add a column (experimental...) - defaults to adding to the end..
8902      * @param {Object} config 
8903     */
8904     addColumn : function(c)
8905     {
8906     
8907         var i = this.config.length;
8908         this.config[i] = c;
8909         
8910         if(typeof c.dataIndex == "undefined"){
8911             c.dataIndex = i;
8912         }
8913         if(typeof c.renderer == "string"){
8914             c.renderer = Roo.util.Format[c.renderer];
8915         }
8916         if(typeof c.id == "undefined"){
8917             c.id = Roo.id();
8918         }
8919         if(c.editor && c.editor.xtype){
8920             c.editor  = Roo.factory(c.editor, Roo.grid);
8921         }
8922         if(c.editor && c.editor.isFormField){
8923             c.editor = new Roo.grid.GridEditor(c.editor);
8924         }
8925         this.lookup[c.id] = c;
8926     }
8927     
8928 });
8929
8930 Roo.grid.ColumnModel.defaultRenderer = function(value)
8931 {
8932     if(typeof value == "object") {
8933         return value;
8934     }
8935         if(typeof value == "string" && value.length < 1){
8936             return "&#160;";
8937         }
8938     
8939         return String.format("{0}", value);
8940 };
8941
8942 // Alias for backwards compatibility
8943 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8944 /*
8945  * Based on:
8946  * Ext JS Library 1.1.1
8947  * Copyright(c) 2006-2007, Ext JS, LLC.
8948  *
8949  * Originally Released Under LGPL - original licence link has changed is not relivant.
8950  *
8951  * Fork - LGPL
8952  * <script type="text/javascript">
8953  */
8954  
8955 /**
8956  * @class Roo.LoadMask
8957  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8958  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8959  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8960  * element's UpdateManager load indicator and will be destroyed after the initial load.
8961  * @constructor
8962  * Create a new LoadMask
8963  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8964  * @param {Object} config The config object
8965  */
8966 Roo.LoadMask = function(el, config){
8967     this.el = Roo.get(el);
8968     Roo.apply(this, config);
8969     if(this.store){
8970         this.store.on('beforeload', this.onBeforeLoad, this);
8971         this.store.on('load', this.onLoad, this);
8972         this.store.on('loadexception', this.onLoadException, this);
8973         this.removeMask = false;
8974     }else{
8975         var um = this.el.getUpdateManager();
8976         um.showLoadIndicator = false; // disable the default indicator
8977         um.on('beforeupdate', this.onBeforeLoad, this);
8978         um.on('update', this.onLoad, this);
8979         um.on('failure', this.onLoad, this);
8980         this.removeMask = true;
8981     }
8982 };
8983
8984 Roo.LoadMask.prototype = {
8985     /**
8986      * @cfg {Boolean} removeMask
8987      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8988      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8989      */
8990     removeMask : false,
8991     /**
8992      * @cfg {String} msg
8993      * The text to display in a centered loading message box (defaults to 'Loading...')
8994      */
8995     msg : 'Loading...',
8996     /**
8997      * @cfg {String} msgCls
8998      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8999      */
9000     msgCls : 'x-mask-loading',
9001
9002     /**
9003      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9004      * @type Boolean
9005      */
9006     disabled: false,
9007
9008     /**
9009      * Disables the mask to prevent it from being displayed
9010      */
9011     disable : function(){
9012        this.disabled = true;
9013     },
9014
9015     /**
9016      * Enables the mask so that it can be displayed
9017      */
9018     enable : function(){
9019         this.disabled = false;
9020     },
9021     
9022     onLoadException : function()
9023     {
9024         Roo.log(arguments);
9025         
9026         if (typeof(arguments[3]) != 'undefined') {
9027             Roo.MessageBox.alert("Error loading",arguments[3]);
9028         } 
9029         /*
9030         try {
9031             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9032                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9033             }   
9034         } catch(e) {
9035             
9036         }
9037         */
9038     
9039         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9040     },
9041     // private
9042     onLoad : function()
9043     {
9044         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9045     },
9046
9047     // private
9048     onBeforeLoad : function(){
9049         if(!this.disabled){
9050             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9051         }
9052     },
9053
9054     // private
9055     destroy : function(){
9056         if(this.store){
9057             this.store.un('beforeload', this.onBeforeLoad, this);
9058             this.store.un('load', this.onLoad, this);
9059             this.store.un('loadexception', this.onLoadException, this);
9060         }else{
9061             var um = this.el.getUpdateManager();
9062             um.un('beforeupdate', this.onBeforeLoad, this);
9063             um.un('update', this.onLoad, this);
9064             um.un('failure', this.onLoad, this);
9065         }
9066     }
9067 };/**
9068  * @class Roo.bootstrap.Table
9069  * @licence LGBL
9070  * @extends Roo.bootstrap.Component
9071  * @children Roo.bootstrap.TableBody
9072  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9073  * Similar to Roo.grid.Grid
9074  * <pre><code>
9075  var table = Roo.factory({
9076     xtype : 'Table',
9077     xns : Roo.bootstrap,
9078     autoSizeColumns: true,
9079     
9080     
9081     store : {
9082         xtype : 'Store',
9083         xns : Roo.data,
9084         remoteSort : true,
9085         sortInfo : { direction : 'ASC', field: 'name' },
9086         proxy : {
9087            xtype : 'HttpProxy',
9088            xns : Roo.data,
9089            method : 'GET',
9090            url : 'https://example.com/some.data.url.json'
9091         },
9092         reader : {
9093            xtype : 'JsonReader',
9094            xns : Roo.data,
9095            fields : [ 'id', 'name', whatever' ],
9096            id : 'id',
9097            root : 'data'
9098         }
9099     },
9100     cm : [
9101         {
9102             xtype : 'ColumnModel',
9103             xns : Roo.grid,
9104             align : 'center',
9105             cursor : 'pointer',
9106             dataIndex : 'is_in_group',
9107             header : "Name",
9108             sortable : true,
9109             renderer : function(v, x , r) {  
9110             
9111                 return String.format("{0}", v)
9112             }
9113             width : 3
9114         } // more columns..
9115     ],
9116     selModel : {
9117         xtype : 'RowSelectionModel',
9118         xns : Roo.bootstrap.Table
9119         // you can add listeners to catch selection change here....
9120     }
9121      
9122
9123  });
9124  // set any options
9125  grid.render(Roo.get("some-div"));
9126 </code></pre>
9127
9128 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9129
9130
9131
9132  *
9133  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9134  * @cfg {Roo.data.Store} store The data store to use
9135  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9136  * 
9137  * @cfg {String} cls table class
9138  *
9139  * 
9140  * @cfg {boolean} striped Should the rows be alternative striped
9141  * @cfg {boolean} bordered Add borders to the table
9142  * @cfg {boolean} hover Add hover highlighting
9143  * @cfg {boolean} condensed Format condensed
9144  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9145  *                also adds table-responsive (see bootstrap docs for details)
9146  * @cfg {Boolean} loadMask (true|false) default false
9147  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9148  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9149  * @cfg {Boolean} rowSelection (true|false) default false
9150  * @cfg {Boolean} cellSelection (true|false) default false
9151  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9152  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9153  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9154  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9155  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9156  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9157  * 
9158  * @constructor
9159  * Create a new Table
9160  * @param {Object} config The config object
9161  */
9162
9163 Roo.bootstrap.Table = function(config)
9164 {
9165     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9166      
9167     // BC...
9168     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9169     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9170     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9171     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9172     
9173     this.view = this; // compat with grid.
9174     
9175     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9176     if (this.sm) {
9177         this.sm.grid = this;
9178         this.selModel = Roo.factory(this.sm, Roo.grid);
9179         this.sm = this.selModel;
9180         this.sm.xmodule = this.xmodule || false;
9181     }
9182     
9183     if (this.cm && typeof(this.cm.config) == 'undefined') {
9184         this.colModel = new Roo.grid.ColumnModel(this.cm);
9185         this.cm = this.colModel;
9186         this.cm.xmodule = this.xmodule || false;
9187     }
9188     if (this.store) {
9189         this.store= Roo.factory(this.store, Roo.data);
9190         this.ds = this.store;
9191         this.ds.xmodule = this.xmodule || false;
9192          
9193     }
9194     if (this.footer && this.store) {
9195         this.footer.dataSource = this.ds;
9196         this.footer = Roo.factory(this.footer);
9197     }
9198     
9199     /** @private */
9200     this.addEvents({
9201         /**
9202          * @event cellclick
9203          * Fires when a cell is clicked
9204          * @param {Roo.bootstrap.Table} this
9205          * @param {Roo.Element} el
9206          * @param {Number} rowIndex
9207          * @param {Number} columnIndex
9208          * @param {Roo.EventObject} e
9209          */
9210         "cellclick" : true,
9211         /**
9212          * @event celldblclick
9213          * Fires when a cell is double clicked
9214          * @param {Roo.bootstrap.Table} this
9215          * @param {Roo.Element} el
9216          * @param {Number} rowIndex
9217          * @param {Number} columnIndex
9218          * @param {Roo.EventObject} e
9219          */
9220         "celldblclick" : true,
9221         /**
9222          * @event rowclick
9223          * Fires when a row is clicked
9224          * @param {Roo.bootstrap.Table} this
9225          * @param {Roo.Element} el
9226          * @param {Number} rowIndex
9227          * @param {Roo.EventObject} e
9228          */
9229         "rowclick" : true,
9230         /**
9231          * @event rowdblclick
9232          * Fires when a row is double clicked
9233          * @param {Roo.bootstrap.Table} this
9234          * @param {Roo.Element} el
9235          * @param {Number} rowIndex
9236          * @param {Roo.EventObject} e
9237          */
9238         "rowdblclick" : true,
9239         /**
9240          * @event mouseover
9241          * Fires when a mouseover occur
9242          * @param {Roo.bootstrap.Table} this
9243          * @param {Roo.Element} el
9244          * @param {Number} rowIndex
9245          * @param {Number} columnIndex
9246          * @param {Roo.EventObject} e
9247          */
9248         "mouseover" : true,
9249         /**
9250          * @event mouseout
9251          * Fires when a mouseout occur
9252          * @param {Roo.bootstrap.Table} this
9253          * @param {Roo.Element} el
9254          * @param {Number} rowIndex
9255          * @param {Number} columnIndex
9256          * @param {Roo.EventObject} e
9257          */
9258         "mouseout" : true,
9259         /**
9260          * @event rowclass
9261          * Fires when a row is rendered, so you can change add a style to it.
9262          * @param {Roo.bootstrap.Table} this
9263          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9264          */
9265         'rowclass' : true,
9266           /**
9267          * @event rowsrendered
9268          * Fires when all the  rows have been rendered
9269          * @param {Roo.bootstrap.Table} this
9270          */
9271         'rowsrendered' : true,
9272         /**
9273          * @event contextmenu
9274          * The raw contextmenu event for the entire grid.
9275          * @param {Roo.EventObject} e
9276          */
9277         "contextmenu" : true,
9278         /**
9279          * @event rowcontextmenu
9280          * Fires when a row is right clicked
9281          * @param {Roo.bootstrap.Table} this
9282          * @param {Number} rowIndex
9283          * @param {Roo.EventObject} e
9284          */
9285         "rowcontextmenu" : true,
9286         /**
9287          * @event cellcontextmenu
9288          * Fires when a cell is right clicked
9289          * @param {Roo.bootstrap.Table} this
9290          * @param {Number} rowIndex
9291          * @param {Number} cellIndex
9292          * @param {Roo.EventObject} e
9293          */
9294          "cellcontextmenu" : true,
9295          /**
9296          * @event headercontextmenu
9297          * Fires when a header is right clicked
9298          * @param {Roo.bootstrap.Table} this
9299          * @param {Number} columnIndex
9300          * @param {Roo.EventObject} e
9301          */
9302         "headercontextmenu" : true,
9303         /**
9304          * @event mousedown
9305          * The raw mousedown event for the entire grid.
9306          * @param {Roo.EventObject} e
9307          */
9308         "mousedown" : true
9309         
9310     });
9311 };
9312
9313 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9314     
9315     cls: false,
9316     
9317     striped : false,
9318     scrollBody : false,
9319     bordered: false,
9320     hover:  false,
9321     condensed : false,
9322     responsive : false,
9323     sm : false,
9324     cm : false,
9325     store : false,
9326     loadMask : false,
9327     footerShow : true,
9328     headerShow : true,
9329     enableColumnResize: true,
9330   
9331     rowSelection : false,
9332     cellSelection : false,
9333     layout : false,
9334
9335     minColumnWidth : 50,
9336     
9337     // Roo.Element - the tbody
9338     bodyEl: false,  // <tbody> Roo.Element - thead element    
9339     headEl: false,  // <thead> Roo.Element - thead element
9340     resizeProxy : false, // proxy element for dragging?
9341
9342
9343     
9344     container: false, // used by gridpanel...
9345     
9346     lazyLoad : false,
9347     
9348     CSS : Roo.util.CSS,
9349     
9350     auto_hide_footer : false,
9351     
9352     view: false, // actually points to this..
9353     
9354     getAutoCreate : function()
9355     {
9356         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9357         
9358         cfg = {
9359             tag: 'table',
9360             cls : 'table', 
9361             cn : []
9362         };
9363         // this get's auto added by panel.Grid
9364         if (this.scrollBody) {
9365             cfg.cls += ' table-body-fixed';
9366         }    
9367         if (this.striped) {
9368             cfg.cls += ' table-striped';
9369         }
9370         
9371         if (this.hover) {
9372             cfg.cls += ' table-hover';
9373         }
9374         if (this.bordered) {
9375             cfg.cls += ' table-bordered';
9376         }
9377         if (this.condensed) {
9378             cfg.cls += ' table-condensed';
9379         }
9380         
9381         if (this.responsive) {
9382             cfg.cls += ' table-responsive';
9383         }
9384         
9385         if (this.cls) {
9386             cfg.cls+=  ' ' +this.cls;
9387         }
9388         
9389         
9390         
9391         if (this.layout) {
9392             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9393         }
9394         
9395         if(this.store || this.cm){
9396             if(this.headerShow){
9397                 cfg.cn.push(this.renderHeader());
9398             }
9399             
9400             cfg.cn.push(this.renderBody());
9401             
9402             if(this.footerShow){
9403                 cfg.cn.push(this.renderFooter());
9404             }
9405             // where does this come from?
9406             //cfg.cls+=  ' TableGrid';
9407         }
9408         
9409         return { cn : [ cfg ] };
9410     },
9411     
9412     initEvents : function()
9413     {   
9414         if(!this.store || !this.cm){
9415             return;
9416         }
9417         if (this.selModel) {
9418             this.selModel.initEvents();
9419         }
9420         
9421         
9422         //Roo.log('initEvents with ds!!!!');
9423         
9424         this.bodyEl = this.el.select('tbody', true).first();
9425         this.headEl = this.el.select('thead', true).first();
9426         this.mainFoot = this.el.select('tfoot', true).first();
9427         
9428         
9429         
9430         
9431         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9432             e.on('click', this.sort, this);
9433         }, this);
9434         
9435         
9436         // why is this done????? = it breaks dialogs??
9437         //this.parent().el.setStyle('position', 'relative');
9438         
9439         
9440         if (this.footer) {
9441             this.footer.parentId = this.id;
9442             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9443             
9444             if(this.lazyLoad){
9445                 this.el.select('tfoot tr td').first().addClass('hide');
9446             }
9447         } 
9448         
9449         if(this.loadMask) {
9450             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9451         }
9452         
9453         this.store.on('load', this.onLoad, this);
9454         this.store.on('beforeload', this.onBeforeLoad, this);
9455         this.store.on('update', this.onUpdate, this);
9456         this.store.on('add', this.onAdd, this);
9457         this.store.on("clear", this.clear, this);
9458         
9459         this.el.on("contextmenu", this.onContextMenu, this);
9460         
9461         
9462         this.cm.on("headerchange", this.onHeaderChange, this);
9463         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9464
9465  //?? does bodyEl get replaced on render?
9466         this.bodyEl.on("click", this.onClick, this);
9467         this.bodyEl.on("dblclick", this.onDblClick, this);        
9468         this.bodyEl.on('scroll', this.onBodyScroll, this);
9469
9470         // guessing mainbody will work - this relays usually caught by selmodel at present.
9471         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9472   
9473   
9474         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9475         
9476   
9477         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9478             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9479         }
9480         
9481         this.initCSS();
9482     },
9483     // Compatibility with grid - we implement all the view features at present.
9484     getView : function()
9485     {
9486         return this;
9487     },
9488     
9489     initCSS : function()
9490     {
9491         
9492         
9493         var cm = this.cm, styles = [];
9494         this.CSS.removeStyleSheet(this.id + '-cssrules');
9495         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9496         // we can honour xs/sm/md/xl  as widths...
9497         // we first have to decide what widht we are currently at...
9498         var sz = Roo.getGridSize();
9499         
9500         var total = 0;
9501         var last = -1;
9502         var cols = []; // visable cols.
9503         var total_abs = 0;
9504         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9505             var w = cm.getColumnWidth(i, false);
9506             if(cm.isHidden(i)){
9507                 cols.push( { rel : false, abs : 0 });
9508                 continue;
9509             }
9510             if (w !== false) {
9511                 cols.push( { rel : false, abs : w });
9512                 total_abs += w;
9513                 last = i; // not really..
9514                 continue;
9515             }
9516             var w = cm.getColumnWidth(i, sz);
9517             if (w > 0) {
9518                 last = i
9519             }
9520             total += w;
9521             cols.push( { rel : w, abs : false });
9522         }
9523         
9524         var avail = this.bodyEl.dom.clientWidth - total_abs;
9525         
9526         var unitWidth = Math.floor(avail / total);
9527         var rem = avail - (unitWidth * total);
9528         
9529         var hidden, width, pos = 0 , splithide , left;
9530         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9531             
9532             hidden = 'display:none;';
9533             left = '';
9534             width  = 'width:0px;';
9535             splithide = '';
9536             if(!cm.isHidden(i)){
9537                 hidden = '';
9538                 
9539                 
9540                 // we can honour xs/sm/md/xl ?
9541                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9542                 if (w===0) {
9543                     hidden = 'display:none;';
9544                 }
9545                 // width should return a small number...
9546                 if (i == last) {
9547                     w+=rem; // add the remaining with..
9548                 }
9549                 pos += w;
9550                 left = "left:" + (pos -4) + "px;";
9551                 width = "width:" + w+ "px;";
9552                 
9553             }
9554             if (this.responsive) {
9555                 width = '';
9556                 left = '';
9557                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9558                 splithide = 'display: none;';
9559             }
9560             
9561             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9562             if (this.headEl) {
9563                 if (i == last) {
9564                     splithide = 'display:none;';
9565                 }
9566                 
9567                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9568                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9569                 );
9570             }
9571             
9572         }
9573         //Roo.log(styles.join(''));
9574         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9575         
9576     },
9577     
9578     
9579     
9580     onContextMenu : function(e, t)
9581     {
9582         this.processEvent("contextmenu", e);
9583     },
9584     
9585     processEvent : function(name, e)
9586     {
9587         if (name != 'touchstart' ) {
9588             this.fireEvent(name, e);    
9589         }
9590         
9591         var t = e.getTarget();
9592         
9593         var cell = Roo.get(t);
9594         
9595         if(!cell){
9596             return;
9597         }
9598         
9599         if(cell.findParent('tfoot', false, true)){
9600             return;
9601         }
9602         
9603         if(cell.findParent('thead', false, true)){
9604             
9605             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9606                 cell = Roo.get(t).findParent('th', false, true);
9607                 if (!cell) {
9608                     Roo.log("failed to find th in thead?");
9609                     Roo.log(e.getTarget());
9610                     return;
9611                 }
9612             }
9613             
9614             var cellIndex = cell.dom.cellIndex;
9615             
9616             var ename = name == 'touchstart' ? 'click' : name;
9617             this.fireEvent("header" + ename, this, cellIndex, e);
9618             
9619             return;
9620         }
9621         
9622         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9623             cell = Roo.get(t).findParent('td', false, true);
9624             if (!cell) {
9625                 Roo.log("failed to find th in tbody?");
9626                 Roo.log(e.getTarget());
9627                 return;
9628             }
9629         }
9630         
9631         var row = cell.findParent('tr', false, true);
9632         var cellIndex = cell.dom.cellIndex;
9633         var rowIndex = row.dom.rowIndex - 1;
9634         
9635         if(row !== false){
9636             
9637             this.fireEvent("row" + name, this, rowIndex, e);
9638             
9639             if(cell !== false){
9640             
9641                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9642             }
9643         }
9644         
9645     },
9646     
9647     onMouseover : function(e, el)
9648     {
9649         var cell = Roo.get(el);
9650         
9651         if(!cell){
9652             return;
9653         }
9654         
9655         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9656             cell = cell.findParent('td', false, true);
9657         }
9658         
9659         var row = cell.findParent('tr', false, true);
9660         var cellIndex = cell.dom.cellIndex;
9661         var rowIndex = row.dom.rowIndex - 1; // start from 0
9662         
9663         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9664         
9665     },
9666     
9667     onMouseout : function(e, el)
9668     {
9669         var cell = Roo.get(el);
9670         
9671         if(!cell){
9672             return;
9673         }
9674         
9675         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9676             cell = cell.findParent('td', false, true);
9677         }
9678         
9679         var row = cell.findParent('tr', false, true);
9680         var cellIndex = cell.dom.cellIndex;
9681         var rowIndex = row.dom.rowIndex - 1; // start from 0
9682         
9683         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9684         
9685     },
9686     
9687     onClick : function(e, el)
9688     {
9689         var cell = Roo.get(el);
9690         
9691         if(!cell || (!this.cellSelection && !this.rowSelection)){
9692             return;
9693         }
9694         
9695         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9696             cell = cell.findParent('td', false, true);
9697         }
9698         
9699         if(!cell || typeof(cell) == 'undefined'){
9700             return;
9701         }
9702         
9703         var row = cell.findParent('tr', false, true);
9704         
9705         if(!row || typeof(row) == 'undefined'){
9706             return;
9707         }
9708         
9709         var cellIndex = cell.dom.cellIndex;
9710         var rowIndex = this.getRowIndex(row);
9711         
9712         // why??? - should these not be based on SelectionModel?
9713         //if(this.cellSelection){
9714             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9715         //}
9716         
9717         //if(this.rowSelection){
9718             this.fireEvent('rowclick', this, row, rowIndex, e);
9719         //}
9720          
9721     },
9722         
9723     onDblClick : function(e,el)
9724     {
9725         var cell = Roo.get(el);
9726         
9727         if(!cell || (!this.cellSelection && !this.rowSelection)){
9728             return;
9729         }
9730         
9731         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9732             cell = cell.findParent('td', false, true);
9733         }
9734         
9735         if(!cell || typeof(cell) == 'undefined'){
9736             return;
9737         }
9738         
9739         var row = cell.findParent('tr', false, true);
9740         
9741         if(!row || typeof(row) == 'undefined'){
9742             return;
9743         }
9744         
9745         var cellIndex = cell.dom.cellIndex;
9746         var rowIndex = this.getRowIndex(row);
9747         
9748         if(this.cellSelection){
9749             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9750         }
9751         
9752         if(this.rowSelection){
9753             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9754         }
9755     },
9756     findRowIndex : function(el)
9757     {
9758         var cell = Roo.get(el);
9759         if(!cell) {
9760             return false;
9761         }
9762         var row = cell.findParent('tr', false, true);
9763         
9764         if(!row || typeof(row) == 'undefined'){
9765             return false;
9766         }
9767         return this.getRowIndex(row);
9768     },
9769     sort : function(e,el)
9770     {
9771         var col = Roo.get(el);
9772         
9773         if(!col.hasClass('sortable')){
9774             return;
9775         }
9776         
9777         var sort = col.attr('sort');
9778         var dir = 'ASC';
9779         
9780         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9781             dir = 'DESC';
9782         }
9783         
9784         this.store.sortInfo = {field : sort, direction : dir};
9785         
9786         if (this.footer) {
9787             Roo.log("calling footer first");
9788             this.footer.onClick('first');
9789         } else {
9790         
9791             this.store.load({ params : { start : 0 } });
9792         }
9793     },
9794     
9795     renderHeader : function()
9796     {
9797         var header = {
9798             tag: 'thead',
9799             cn : []
9800         };
9801         
9802         var cm = this.cm;
9803         this.totalWidth = 0;
9804         
9805         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9806             
9807             var config = cm.config[i];
9808             
9809             var c = {
9810                 tag: 'th',
9811                 cls : 'x-hcol-' + i,
9812                 style : '',
9813                 
9814                 html: cm.getColumnHeader(i)
9815             };
9816             
9817             var tooltip = cm.getColumnTooltip(i);
9818             if (tooltip) {
9819                 c.tooltip = tooltip;
9820             }
9821             
9822             
9823             var hh = '';
9824             
9825             if(typeof(config.sortable) != 'undefined' && config.sortable){
9826                 c.cls += ' sortable';
9827                 c.html = '<i class="fa"></i>' + c.html;
9828             }
9829             
9830             // could use BS4 hidden-..-down 
9831             
9832             if(typeof(config.lgHeader) != 'undefined'){
9833                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9834             }
9835             
9836             if(typeof(config.mdHeader) != 'undefined'){
9837                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9838             }
9839             
9840             if(typeof(config.smHeader) != 'undefined'){
9841                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9842             }
9843             
9844             if(typeof(config.xsHeader) != 'undefined'){
9845                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9846             }
9847             
9848             if(hh.length){
9849                 c.html = hh;
9850             }
9851             
9852             if(typeof(config.tooltip) != 'undefined'){
9853                 c.tooltip = config.tooltip;
9854             }
9855             
9856             if(typeof(config.colspan) != 'undefined'){
9857                 c.colspan = config.colspan;
9858             }
9859             
9860             // hidden is handled by CSS now
9861             
9862             if(typeof(config.dataIndex) != 'undefined'){
9863                 c.sort = config.dataIndex;
9864             }
9865             
9866            
9867             
9868             if(typeof(config.align) != 'undefined' && config.align.length){
9869                 c.style += ' text-align:' + config.align + ';';
9870             }
9871             
9872             /* width is done in CSS
9873              *if(typeof(config.width) != 'undefined'){
9874                 c.style += ' width:' + config.width + 'px;';
9875                 this.totalWidth += config.width;
9876             } else {
9877                 this.totalWidth += 100; // assume minimum of 100 per column?
9878             }
9879             */
9880             
9881             if(typeof(config.cls) != 'undefined'){
9882                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9883             }
9884             // this is the bit that doesnt reall work at all...
9885             
9886             if (this.responsive) {
9887                  
9888             
9889                 ['xs','sm','md','lg'].map(function(size){
9890                     
9891                     if(typeof(config[size]) == 'undefined'){
9892                         return;
9893                     }
9894                      
9895                     if (!config[size]) { // 0 = hidden
9896                         // BS 4 '0' is treated as hide that column and below.
9897                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9898                         return;
9899                     }
9900                     
9901                     c.cls += ' col-' + size + '-' + config[size] + (
9902                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9903                     );
9904                     
9905                     
9906                 });
9907             }
9908             // at the end?
9909             
9910             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9911             
9912             
9913             
9914             
9915             header.cn.push(c)
9916         }
9917         
9918         return header;
9919     },
9920     
9921     renderBody : function()
9922     {
9923         var body = {
9924             tag: 'tbody',
9925             cn : [
9926                 {
9927                     tag: 'tr',
9928                     cn : [
9929                         {
9930                             tag : 'td',
9931                             colspan :  this.cm.getColumnCount()
9932                         }
9933                     ]
9934                 }
9935             ]
9936         };
9937         
9938         return body;
9939     },
9940     
9941     renderFooter : function()
9942     {
9943         var footer = {
9944             tag: 'tfoot',
9945             cn : [
9946                 {
9947                     tag: 'tr',
9948                     cn : [
9949                         {
9950                             tag : 'td',
9951                             colspan :  this.cm.getColumnCount()
9952                         }
9953                     ]
9954                 }
9955             ]
9956         };
9957         
9958         return footer;
9959     },
9960     
9961     
9962     
9963     onLoad : function()
9964     {
9965 //        Roo.log('ds onload');
9966         this.clear();
9967         
9968         var _this = this;
9969         var cm = this.cm;
9970         var ds = this.store;
9971         
9972         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9973             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9974             if (_this.store.sortInfo) {
9975                     
9976                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9977                     e.select('i', true).addClass(['fa-arrow-up']);
9978                 }
9979                 
9980                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9981                     e.select('i', true).addClass(['fa-arrow-down']);
9982                 }
9983             }
9984         });
9985         
9986         var tbody =  this.bodyEl;
9987               
9988         if(ds.getCount() > 0){
9989             ds.data.each(function(d,rowIndex){
9990                 var row =  this.renderRow(cm, ds, rowIndex);
9991                 
9992                 tbody.createChild(row);
9993                 
9994                 var _this = this;
9995                 
9996                 if(row.cellObjects.length){
9997                     Roo.each(row.cellObjects, function(r){
9998                         _this.renderCellObject(r);
9999                     })
10000                 }
10001                 
10002             }, this);
10003         }
10004         
10005         var tfoot = this.el.select('tfoot', true).first();
10006         
10007         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10008             
10009             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10010             
10011             var total = this.ds.getTotalCount();
10012             
10013             if(this.footer.pageSize < total){
10014                 this.mainFoot.show();
10015             }
10016         }
10017         
10018         Roo.each(this.el.select('tbody td', true).elements, function(e){
10019             e.on('mouseover', _this.onMouseover, _this);
10020         });
10021         
10022         Roo.each(this.el.select('tbody td', true).elements, function(e){
10023             e.on('mouseout', _this.onMouseout, _this);
10024         });
10025         this.fireEvent('rowsrendered', this);
10026         
10027         this.autoSize();
10028         
10029         this.initCSS(); /// resize cols
10030
10031         
10032     },
10033     
10034     
10035     onUpdate : function(ds,record)
10036     {
10037         this.refreshRow(record);
10038         this.autoSize();
10039     },
10040     
10041     onRemove : function(ds, record, index, isUpdate){
10042         if(isUpdate !== true){
10043             this.fireEvent("beforerowremoved", this, index, record);
10044         }
10045         var bt = this.bodyEl.dom;
10046         
10047         var rows = this.el.select('tbody > tr', true).elements;
10048         
10049         if(typeof(rows[index]) != 'undefined'){
10050             bt.removeChild(rows[index].dom);
10051         }
10052         
10053 //        if(bt.rows[index]){
10054 //            bt.removeChild(bt.rows[index]);
10055 //        }
10056         
10057         if(isUpdate !== true){
10058             //this.stripeRows(index);
10059             //this.syncRowHeights(index, index);
10060             //this.layout();
10061             this.fireEvent("rowremoved", this, index, record);
10062         }
10063     },
10064     
10065     onAdd : function(ds, records, rowIndex)
10066     {
10067         //Roo.log('on Add called');
10068         // - note this does not handle multiple adding very well..
10069         var bt = this.bodyEl.dom;
10070         for (var i =0 ; i < records.length;i++) {
10071             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10072             //Roo.log(records[i]);
10073             //Roo.log(this.store.getAt(rowIndex+i));
10074             this.insertRow(this.store, rowIndex + i, false);
10075             return;
10076         }
10077         
10078     },
10079     
10080     
10081     refreshRow : function(record){
10082         var ds = this.store, index;
10083         if(typeof record == 'number'){
10084             index = record;
10085             record = ds.getAt(index);
10086         }else{
10087             index = ds.indexOf(record);
10088             if (index < 0) {
10089                 return; // should not happen - but seems to 
10090             }
10091         }
10092         this.insertRow(ds, index, true);
10093         this.autoSize();
10094         this.onRemove(ds, record, index+1, true);
10095         this.autoSize();
10096         //this.syncRowHeights(index, index);
10097         //this.layout();
10098         this.fireEvent("rowupdated", this, index, record);
10099     },
10100     // private - called by RowSelection
10101     onRowSelect : function(rowIndex){
10102         var row = this.getRowDom(rowIndex);
10103         row.addClass(['bg-info','info']);
10104     },
10105     // private - called by RowSelection
10106     onRowDeselect : function(rowIndex)
10107     {
10108         if (rowIndex < 0) {
10109             return;
10110         }
10111         var row = this.getRowDom(rowIndex);
10112         row.removeClass(['bg-info','info']);
10113     },
10114       /**
10115      * Focuses the specified row.
10116      * @param {Number} row The row index
10117      */
10118     focusRow : function(row)
10119     {
10120         //Roo.log('GridView.focusRow');
10121         var x = this.bodyEl.dom.scrollLeft;
10122         this.focusCell(row, 0, false);
10123         this.bodyEl.dom.scrollLeft = x;
10124
10125     },
10126      /**
10127      * Focuses the specified cell.
10128      * @param {Number} row The row index
10129      * @param {Number} col The column index
10130      * @param {Boolean} hscroll false to disable horizontal scrolling
10131      */
10132     focusCell : function(row, col, hscroll)
10133     {
10134         //Roo.log('GridView.focusCell');
10135         var el = this.ensureVisible(row, col, hscroll);
10136         // not sure what focusEL achives = it's a <a> pos relative 
10137         //this.focusEl.alignTo(el, "tl-tl");
10138         //if(Roo.isGecko){
10139         //    this.focusEl.focus();
10140         //}else{
10141         //    this.focusEl.focus.defer(1, this.focusEl);
10142         //}
10143     },
10144     
10145      /**
10146      * Scrolls the specified cell into view
10147      * @param {Number} row The row index
10148      * @param {Number} col The column index
10149      * @param {Boolean} hscroll false to disable horizontal scrolling
10150      */
10151     ensureVisible : function(row, col, hscroll)
10152     {
10153         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10154         //return null; //disable for testing.
10155         if(typeof row != "number"){
10156             row = row.rowIndex;
10157         }
10158         if(row < 0 && row >= this.ds.getCount()){
10159             return  null;
10160         }
10161         col = (col !== undefined ? col : 0);
10162         var cm = this.cm;
10163         while(cm.isHidden(col)){
10164             col++;
10165         }
10166
10167         var el = this.getCellDom(row, col);
10168         if(!el){
10169             return null;
10170         }
10171         var c = this.bodyEl.dom;
10172
10173         var ctop = parseInt(el.offsetTop, 10);
10174         var cleft = parseInt(el.offsetLeft, 10);
10175         var cbot = ctop + el.offsetHeight;
10176         var cright = cleft + el.offsetWidth;
10177
10178         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10179         var ch = 0; //?? header is not withing the area?
10180         var stop = parseInt(c.scrollTop, 10);
10181         var sleft = parseInt(c.scrollLeft, 10);
10182         var sbot = stop + ch;
10183         var sright = sleft + c.clientWidth;
10184         /*
10185         Roo.log('GridView.ensureVisible:' +
10186                 ' ctop:' + ctop +
10187                 ' c.clientHeight:' + c.clientHeight +
10188                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10189                 ' stop:' + stop +
10190                 ' cbot:' + cbot +
10191                 ' sbot:' + sbot +
10192                 ' ch:' + ch  
10193                 );
10194         */
10195         if(ctop < stop){
10196             c.scrollTop = ctop;
10197             //Roo.log("set scrolltop to ctop DISABLE?");
10198         }else if(cbot > sbot){
10199             //Roo.log("set scrolltop to cbot-ch");
10200             c.scrollTop = cbot-ch;
10201         }
10202
10203         if(hscroll !== false){
10204             if(cleft < sleft){
10205                 c.scrollLeft = cleft;
10206             }else if(cright > sright){
10207                 c.scrollLeft = cright-c.clientWidth;
10208             }
10209         }
10210
10211         return el;
10212     },
10213     
10214     
10215     insertRow : function(dm, rowIndex, isUpdate){
10216         
10217         if(!isUpdate){
10218             this.fireEvent("beforerowsinserted", this, rowIndex);
10219         }
10220             //var s = this.getScrollState();
10221         var row = this.renderRow(this.cm, this.store, rowIndex);
10222         // insert before rowIndex..
10223         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10224         
10225         var _this = this;
10226                 
10227         if(row.cellObjects.length){
10228             Roo.each(row.cellObjects, function(r){
10229                 _this.renderCellObject(r);
10230             })
10231         }
10232             
10233         if(!isUpdate){
10234             this.fireEvent("rowsinserted", this, rowIndex);
10235             //this.syncRowHeights(firstRow, lastRow);
10236             //this.stripeRows(firstRow);
10237             //this.layout();
10238         }
10239         
10240     },
10241     
10242     
10243     getRowDom : function(rowIndex)
10244     {
10245         var rows = this.el.select('tbody > tr', true).elements;
10246         
10247         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10248         
10249     },
10250     getCellDom : function(rowIndex, colIndex)
10251     {
10252         var row = this.getRowDom(rowIndex);
10253         if (row === false) {
10254             return false;
10255         }
10256         var cols = row.select('td', true).elements;
10257         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10258         
10259     },
10260     
10261     // returns the object tree for a tr..
10262   
10263     
10264     renderRow : function(cm, ds, rowIndex) 
10265     {
10266         var d = ds.getAt(rowIndex);
10267         
10268         var row = {
10269             tag : 'tr',
10270             cls : 'x-row-' + rowIndex,
10271             cn : []
10272         };
10273             
10274         var cellObjects = [];
10275         
10276         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10277             var config = cm.config[i];
10278             
10279             var renderer = cm.getRenderer(i);
10280             var value = '';
10281             var id = false;
10282             
10283             if(typeof(renderer) !== 'undefined'){
10284                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10285             }
10286             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10287             // and are rendered into the cells after the row is rendered - using the id for the element.
10288             
10289             if(typeof(value) === 'object'){
10290                 id = Roo.id();
10291                 cellObjects.push({
10292                     container : id,
10293                     cfg : value 
10294                 })
10295             }
10296             
10297             var rowcfg = {
10298                 record: d,
10299                 rowIndex : rowIndex,
10300                 colIndex : i,
10301                 rowClass : ''
10302             };
10303
10304             this.fireEvent('rowclass', this, rowcfg);
10305             
10306             var td = {
10307                 tag: 'td',
10308                 // this might end up displaying HTML?
10309                 // this is too messy... - better to only do it on columsn you know are going to be too long
10310                 //tooltip : (typeof(value) === 'object') ? '' : value,
10311                 cls : rowcfg.rowClass + ' x-col-' + i,
10312                 style: '',
10313                 html: (typeof(value) === 'object') ? '' : value
10314             };
10315             
10316             if (id) {
10317                 td.id = id;
10318             }
10319             
10320             if(typeof(config.colspan) != 'undefined'){
10321                 td.colspan = config.colspan;
10322             }
10323             
10324             
10325             
10326             if(typeof(config.align) != 'undefined' && config.align.length){
10327                 td.style += ' text-align:' + config.align + ';';
10328             }
10329             if(typeof(config.valign) != 'undefined' && config.valign.length){
10330                 td.style += ' vertical-align:' + config.valign + ';';
10331             }
10332             /*
10333             if(typeof(config.width) != 'undefined'){
10334                 td.style += ' width:' +  config.width + 'px;';
10335             }
10336             */
10337             
10338             if(typeof(config.cursor) != 'undefined'){
10339                 td.style += ' cursor:' +  config.cursor + ';';
10340             }
10341             
10342             if(typeof(config.cls) != 'undefined'){
10343                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10344             }
10345             if (this.responsive) {
10346                 ['xs','sm','md','lg'].map(function(size){
10347                     
10348                     if(typeof(config[size]) == 'undefined'){
10349                         return;
10350                     }
10351                     
10352                     
10353                       
10354                     if (!config[size]) { // 0 = hidden
10355                         // BS 4 '0' is treated as hide that column and below.
10356                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10357                         return;
10358                     }
10359                     
10360                     td.cls += ' col-' + size + '-' + config[size] + (
10361                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10362                     );
10363                      
10364     
10365                 });
10366             }
10367             row.cn.push(td);
10368            
10369         }
10370         
10371         row.cellObjects = cellObjects;
10372         
10373         return row;
10374           
10375     },
10376     
10377     
10378     
10379     onBeforeLoad : function()
10380     {
10381         
10382     },
10383      /**
10384      * Remove all rows
10385      */
10386     clear : function()
10387     {
10388         this.el.select('tbody', true).first().dom.innerHTML = '';
10389     },
10390     /**
10391      * Show or hide a row.
10392      * @param {Number} rowIndex to show or hide
10393      * @param {Boolean} state hide
10394      */
10395     setRowVisibility : function(rowIndex, state)
10396     {
10397         var bt = this.bodyEl.dom;
10398         
10399         var rows = this.el.select('tbody > tr', true).elements;
10400         
10401         if(typeof(rows[rowIndex]) == 'undefined'){
10402             return;
10403         }
10404         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10405         
10406     },
10407     
10408     
10409     getSelectionModel : function(){
10410         if(!this.selModel){
10411             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10412         }
10413         return this.selModel;
10414     },
10415     /*
10416      * Render the Roo.bootstrap object from renderder
10417      */
10418     renderCellObject : function(r)
10419     {
10420         var _this = this;
10421         
10422         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10423         
10424         var t = r.cfg.render(r.container);
10425         
10426         if(r.cfg.cn){
10427             Roo.each(r.cfg.cn, function(c){
10428                 var child = {
10429                     container: t.getChildContainer(),
10430                     cfg: c
10431                 };
10432                 _this.renderCellObject(child);
10433             })
10434         }
10435     },
10436     /**
10437      * get the Row Index from a dom element.
10438      * @param {Roo.Element} row The row to look for
10439      * @returns {Number} the row
10440      */
10441     getRowIndex : function(row)
10442     {
10443         var rowIndex = -1;
10444         
10445         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10446             if(el != row){
10447                 return;
10448             }
10449             
10450             rowIndex = index;
10451         });
10452         
10453         return rowIndex;
10454     },
10455     /**
10456      * get the header TH element for columnIndex
10457      * @param {Number} columnIndex
10458      * @returns {Roo.Element}
10459      */
10460     getHeaderIndex: function(colIndex)
10461     {
10462         var cols = this.headEl.select('th', true).elements;
10463         return cols[colIndex]; 
10464     },
10465     /**
10466      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10467      * @param {domElement} cell to look for
10468      * @returns {Number} the column
10469      */
10470     getCellIndex : function(cell)
10471     {
10472         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10473         if(id){
10474             return parseInt(id[1], 10);
10475         }
10476         return 0;
10477     },
10478      /**
10479      * Returns the grid's underlying element = used by panel.Grid
10480      * @return {Element} The element
10481      */
10482     getGridEl : function(){
10483         return this.el;
10484     },
10485      /**
10486      * Forces a resize - used by panel.Grid
10487      * @return {Element} The element
10488      */
10489     autoSize : function()
10490     {
10491         //var ctr = Roo.get(this.container.dom.parentElement);
10492         var ctr = Roo.get(this.el.dom);
10493         
10494         var thd = this.getGridEl().select('thead',true).first();
10495         var tbd = this.getGridEl().select('tbody', true).first();
10496         var tfd = this.getGridEl().select('tfoot', true).first();
10497         
10498         var cw = ctr.getWidth();
10499         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10500         
10501         if (tbd) {
10502             
10503             tbd.setWidth(ctr.getWidth());
10504             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10505             // this needs fixing for various usage - currently only hydra job advers I think..
10506             //tdb.setHeight(
10507             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10508             //); 
10509             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10510             cw -= barsize;
10511         }
10512         cw = Math.max(cw, this.totalWidth);
10513         this.getGridEl().select('tbody tr',true).setWidth(cw);
10514         this.initCSS();
10515         
10516         // resize 'expandable coloumn?
10517         
10518         return; // we doe not have a view in this design..
10519         
10520     },
10521     onBodyScroll: function()
10522     {
10523         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10524         if(this.headEl){
10525             this.headEl.setStyle({
10526                 'position' : 'relative',
10527                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10528             });
10529         }
10530         
10531         if(this.lazyLoad){
10532             
10533             var scrollHeight = this.bodyEl.dom.scrollHeight;
10534             
10535             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10536             
10537             var height = this.bodyEl.getHeight();
10538             
10539             if(scrollHeight - height == scrollTop) {
10540                 
10541                 var total = this.ds.getTotalCount();
10542                 
10543                 if(this.footer.cursor + this.footer.pageSize < total){
10544                     
10545                     this.footer.ds.load({
10546                         params : {
10547                             start : this.footer.cursor + this.footer.pageSize,
10548                             limit : this.footer.pageSize
10549                         },
10550                         add : true
10551                     });
10552                 }
10553             }
10554             
10555         }
10556     },
10557     onColumnSplitterMoved : function(i, diff)
10558     {
10559         this.userResized = true;
10560         
10561         var cm = this.colModel;
10562         
10563         var w = this.getHeaderIndex(i).getWidth() + diff;
10564         
10565         
10566         cm.setColumnWidth(i, w, true);
10567         this.initCSS();
10568         //var cid = cm.getColumnId(i); << not used in this version?
10569        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10570         
10571         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10572         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10573         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10574 */
10575         //this.updateSplitters();
10576         //this.layout(); << ??
10577         this.fireEvent("columnresize", i, w);
10578     },
10579     onHeaderChange : function()
10580     {
10581         var header = this.renderHeader();
10582         var table = this.el.select('table', true).first();
10583         
10584         this.headEl.remove();
10585         this.headEl = table.createChild(header, this.bodyEl, false);
10586         
10587         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10588             e.on('click', this.sort, this);
10589         }, this);
10590         
10591         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10592             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10593         }
10594         
10595     },
10596     
10597     onHiddenChange : function(colModel, colIndex, hidden)
10598     {
10599         /*
10600         this.cm.setHidden()
10601         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10602         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10603         
10604         this.CSS.updateRule(thSelector, "display", "");
10605         this.CSS.updateRule(tdSelector, "display", "");
10606         
10607         if(hidden){
10608             this.CSS.updateRule(thSelector, "display", "none");
10609             this.CSS.updateRule(tdSelector, "display", "none");
10610         }
10611         */
10612         // onload calls initCSS()
10613         this.onHeaderChange();
10614         this.onLoad();
10615     },
10616     
10617     setColumnWidth: function(col_index, width)
10618     {
10619         // width = "md-2 xs-2..."
10620         if(!this.colModel.config[col_index]) {
10621             return;
10622         }
10623         
10624         var w = width.split(" ");
10625         
10626         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10627         
10628         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10629         
10630         
10631         for(var j = 0; j < w.length; j++) {
10632             
10633             if(!w[j]) {
10634                 continue;
10635             }
10636             
10637             var size_cls = w[j].split("-");
10638             
10639             if(!Number.isInteger(size_cls[1] * 1)) {
10640                 continue;
10641             }
10642             
10643             if(!this.colModel.config[col_index][size_cls[0]]) {
10644                 continue;
10645             }
10646             
10647             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10648                 continue;
10649             }
10650             
10651             h_row[0].classList.replace(
10652                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10653                 "col-"+size_cls[0]+"-"+size_cls[1]
10654             );
10655             
10656             for(var i = 0; i < rows.length; i++) {
10657                 
10658                 var size_cls = w[j].split("-");
10659                 
10660                 if(!Number.isInteger(size_cls[1] * 1)) {
10661                     continue;
10662                 }
10663                 
10664                 if(!this.colModel.config[col_index][size_cls[0]]) {
10665                     continue;
10666                 }
10667                 
10668                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10669                     continue;
10670                 }
10671                 
10672                 rows[i].classList.replace(
10673                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10674                     "col-"+size_cls[0]+"-"+size_cls[1]
10675                 );
10676             }
10677             
10678             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10679         }
10680     }
10681 });
10682
10683 // currently only used to find the split on drag.. 
10684 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10685
10686 /**
10687  * @depricated
10688 */
10689 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10690 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10691 /*
10692  * - LGPL
10693  *
10694  * table cell
10695  * 
10696  */
10697
10698 /**
10699  * @class Roo.bootstrap.TableCell
10700  * @extends Roo.bootstrap.Component
10701  * @children Roo.bootstrap.Component
10702  * @parent Roo.bootstrap.TableRow
10703  * Bootstrap TableCell class
10704  * 
10705  * @cfg {String} html cell contain text
10706  * @cfg {String} cls cell class
10707  * @cfg {String} tag cell tag (td|th) default td
10708  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10709  * @cfg {String} align Aligns the content in a cell
10710  * @cfg {String} axis Categorizes cells
10711  * @cfg {String} bgcolor Specifies the background color of a cell
10712  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10713  * @cfg {Number} colspan Specifies the number of columns a cell should span
10714  * @cfg {String} headers Specifies one or more header cells a cell is related to
10715  * @cfg {Number} height Sets the height of a cell
10716  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10717  * @cfg {Number} rowspan Sets the number of rows a cell should span
10718  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10719  * @cfg {String} valign Vertical aligns the content in a cell
10720  * @cfg {Number} width Specifies the width of a cell
10721  * 
10722  * @constructor
10723  * Create a new TableCell
10724  * @param {Object} config The config object
10725  */
10726
10727 Roo.bootstrap.TableCell = function(config){
10728     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10729 };
10730
10731 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10732     
10733     html: false,
10734     cls: false,
10735     tag: false,
10736     abbr: false,
10737     align: false,
10738     axis: false,
10739     bgcolor: false,
10740     charoff: false,
10741     colspan: false,
10742     headers: false,
10743     height: false,
10744     nowrap: false,
10745     rowspan: false,
10746     scope: false,
10747     valign: false,
10748     width: false,
10749     
10750     
10751     getAutoCreate : function(){
10752         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10753         
10754         cfg = {
10755             tag: 'td'
10756         };
10757         
10758         if(this.tag){
10759             cfg.tag = this.tag;
10760         }
10761         
10762         if (this.html) {
10763             cfg.html=this.html
10764         }
10765         if (this.cls) {
10766             cfg.cls=this.cls
10767         }
10768         if (this.abbr) {
10769             cfg.abbr=this.abbr
10770         }
10771         if (this.align) {
10772             cfg.align=this.align
10773         }
10774         if (this.axis) {
10775             cfg.axis=this.axis
10776         }
10777         if (this.bgcolor) {
10778             cfg.bgcolor=this.bgcolor
10779         }
10780         if (this.charoff) {
10781             cfg.charoff=this.charoff
10782         }
10783         if (this.colspan) {
10784             cfg.colspan=this.colspan
10785         }
10786         if (this.headers) {
10787             cfg.headers=this.headers
10788         }
10789         if (this.height) {
10790             cfg.height=this.height
10791         }
10792         if (this.nowrap) {
10793             cfg.nowrap=this.nowrap
10794         }
10795         if (this.rowspan) {
10796             cfg.rowspan=this.rowspan
10797         }
10798         if (this.scope) {
10799             cfg.scope=this.scope
10800         }
10801         if (this.valign) {
10802             cfg.valign=this.valign
10803         }
10804         if (this.width) {
10805             cfg.width=this.width
10806         }
10807         
10808         
10809         return cfg;
10810     }
10811    
10812 });
10813
10814  
10815
10816  /*
10817  * - LGPL
10818  *
10819  * table row
10820  * 
10821  */
10822
10823 /**
10824  * @class Roo.bootstrap.TableRow
10825  * @extends Roo.bootstrap.Component
10826  * @children Roo.bootstrap.TableCell
10827  * @parent Roo.bootstrap.TableBody
10828  * Bootstrap TableRow class
10829  * @cfg {String} cls row class
10830  * @cfg {String} align Aligns the content in a table row
10831  * @cfg {String} bgcolor Specifies a background color for a table row
10832  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10833  * @cfg {String} valign Vertical aligns the content in a table row
10834  * 
10835  * @constructor
10836  * Create a new TableRow
10837  * @param {Object} config The config object
10838  */
10839
10840 Roo.bootstrap.TableRow = function(config){
10841     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10842 };
10843
10844 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10845     
10846     cls: false,
10847     align: false,
10848     bgcolor: false,
10849     charoff: false,
10850     valign: false,
10851     
10852     getAutoCreate : function(){
10853         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10854         
10855         cfg = {
10856             tag: 'tr'
10857         };
10858             
10859         if(this.cls){
10860             cfg.cls = this.cls;
10861         }
10862         if(this.align){
10863             cfg.align = this.align;
10864         }
10865         if(this.bgcolor){
10866             cfg.bgcolor = this.bgcolor;
10867         }
10868         if(this.charoff){
10869             cfg.charoff = this.charoff;
10870         }
10871         if(this.valign){
10872             cfg.valign = this.valign;
10873         }
10874         
10875         return cfg;
10876     }
10877    
10878 });
10879
10880  
10881
10882  /*
10883  * - LGPL
10884  *
10885  * table body
10886  * 
10887  */
10888
10889 /**
10890  * @class Roo.bootstrap.TableBody
10891  * @extends Roo.bootstrap.Component
10892  * @children Roo.bootstrap.TableRow
10893  * @parent Roo.bootstrap.Table
10894  * Bootstrap TableBody class
10895  * @cfg {String} cls element class
10896  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10897  * @cfg {String} align Aligns the content inside the element
10898  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10899  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10900  * 
10901  * @constructor
10902  * Create a new TableBody
10903  * @param {Object} config The config object
10904  */
10905
10906 Roo.bootstrap.TableBody = function(config){
10907     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10908 };
10909
10910 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10911     
10912     cls: false,
10913     tag: false,
10914     align: false,
10915     charoff: false,
10916     valign: false,
10917     
10918     getAutoCreate : function(){
10919         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10920         
10921         cfg = {
10922             tag: 'tbody'
10923         };
10924             
10925         if (this.cls) {
10926             cfg.cls=this.cls
10927         }
10928         if(this.tag){
10929             cfg.tag = this.tag;
10930         }
10931         
10932         if(this.align){
10933             cfg.align = this.align;
10934         }
10935         if(this.charoff){
10936             cfg.charoff = this.charoff;
10937         }
10938         if(this.valign){
10939             cfg.valign = this.valign;
10940         }
10941         
10942         return cfg;
10943     }
10944     
10945     
10946 //    initEvents : function()
10947 //    {
10948 //        
10949 //        if(!this.store){
10950 //            return;
10951 //        }
10952 //        
10953 //        this.store = Roo.factory(this.store, Roo.data);
10954 //        this.store.on('load', this.onLoad, this);
10955 //        
10956 //        this.store.load();
10957 //        
10958 //    },
10959 //    
10960 //    onLoad: function () 
10961 //    {   
10962 //        this.fireEvent('load', this);
10963 //    }
10964 //    
10965 //   
10966 });
10967
10968  
10969
10970  /*
10971  * Based on:
10972  * Ext JS Library 1.1.1
10973  * Copyright(c) 2006-2007, Ext JS, LLC.
10974  *
10975  * Originally Released Under LGPL - original licence link has changed is not relivant.
10976  *
10977  * Fork - LGPL
10978  * <script type="text/javascript">
10979  */
10980
10981 // as we use this in bootstrap.
10982 Roo.namespace('Roo.form');
10983  /**
10984  * @class Roo.form.Action
10985  * Internal Class used to handle form actions
10986  * @constructor
10987  * @param {Roo.form.BasicForm} el The form element or its id
10988  * @param {Object} config Configuration options
10989  */
10990
10991  
10992  
10993 // define the action interface
10994 Roo.form.Action = function(form, options){
10995     this.form = form;
10996     this.options = options || {};
10997 };
10998 /**
10999  * Client Validation Failed
11000  * @const 
11001  */
11002 Roo.form.Action.CLIENT_INVALID = 'client';
11003 /**
11004  * Server Validation Failed
11005  * @const 
11006  */
11007 Roo.form.Action.SERVER_INVALID = 'server';
11008  /**
11009  * Connect to Server Failed
11010  * @const 
11011  */
11012 Roo.form.Action.CONNECT_FAILURE = 'connect';
11013 /**
11014  * Reading Data from Server Failed
11015  * @const 
11016  */
11017 Roo.form.Action.LOAD_FAILURE = 'load';
11018
11019 Roo.form.Action.prototype = {
11020     type : 'default',
11021     failureType : undefined,
11022     response : undefined,
11023     result : undefined,
11024
11025     // interface method
11026     run : function(options){
11027
11028     },
11029
11030     // interface method
11031     success : function(response){
11032
11033     },
11034
11035     // interface method
11036     handleResponse : function(response){
11037
11038     },
11039
11040     // default connection failure
11041     failure : function(response){
11042         
11043         this.response = response;
11044         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11045         this.form.afterAction(this, false);
11046     },
11047
11048     processResponse : function(response){
11049         this.response = response;
11050         if(!response.responseText){
11051             return true;
11052         }
11053         this.result = this.handleResponse(response);
11054         return this.result;
11055     },
11056
11057     // utility functions used internally
11058     getUrl : function(appendParams){
11059         var url = this.options.url || this.form.url || this.form.el.dom.action;
11060         if(appendParams){
11061             var p = this.getParams();
11062             if(p){
11063                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11064             }
11065         }
11066         return url;
11067     },
11068
11069     getMethod : function(){
11070         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11071     },
11072
11073     getParams : function(){
11074         var bp = this.form.baseParams;
11075         var p = this.options.params;
11076         if(p){
11077             if(typeof p == "object"){
11078                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11079             }else if(typeof p == 'string' && bp){
11080                 p += '&' + Roo.urlEncode(bp);
11081             }
11082         }else if(bp){
11083             p = Roo.urlEncode(bp);
11084         }
11085         return p;
11086     },
11087
11088     createCallback : function(){
11089         return {
11090             success: this.success,
11091             failure: this.failure,
11092             scope: this,
11093             timeout: (this.form.timeout*1000),
11094             upload: this.form.fileUpload ? this.success : undefined
11095         };
11096     }
11097 };
11098
11099 Roo.form.Action.Submit = function(form, options){
11100     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11101 };
11102
11103 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11104     type : 'submit',
11105
11106     haveProgress : false,
11107     uploadComplete : false,
11108     
11109     // uploadProgress indicator.
11110     uploadProgress : function()
11111     {
11112         if (!this.form.progressUrl) {
11113             return;
11114         }
11115         
11116         if (!this.haveProgress) {
11117             Roo.MessageBox.progress("Uploading", "Uploading");
11118         }
11119         if (this.uploadComplete) {
11120            Roo.MessageBox.hide();
11121            return;
11122         }
11123         
11124         this.haveProgress = true;
11125    
11126         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11127         
11128         var c = new Roo.data.Connection();
11129         c.request({
11130             url : this.form.progressUrl,
11131             params: {
11132                 id : uid
11133             },
11134             method: 'GET',
11135             success : function(req){
11136                //console.log(data);
11137                 var rdata = false;
11138                 var edata;
11139                 try  {
11140                    rdata = Roo.decode(req.responseText)
11141                 } catch (e) {
11142                     Roo.log("Invalid data from server..");
11143                     Roo.log(edata);
11144                     return;
11145                 }
11146                 if (!rdata || !rdata.success) {
11147                     Roo.log(rdata);
11148                     Roo.MessageBox.alert(Roo.encode(rdata));
11149                     return;
11150                 }
11151                 var data = rdata.data;
11152                 
11153                 if (this.uploadComplete) {
11154                    Roo.MessageBox.hide();
11155                    return;
11156                 }
11157                    
11158                 if (data){
11159                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11160                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11161                     );
11162                 }
11163                 this.uploadProgress.defer(2000,this);
11164             },
11165        
11166             failure: function(data) {
11167                 Roo.log('progress url failed ');
11168                 Roo.log(data);
11169             },
11170             scope : this
11171         });
11172            
11173     },
11174     
11175     
11176     run : function()
11177     {
11178         // run get Values on the form, so it syncs any secondary forms.
11179         this.form.getValues();
11180         
11181         var o = this.options;
11182         var method = this.getMethod();
11183         var isPost = method == 'POST';
11184         if(o.clientValidation === false || this.form.isValid()){
11185             
11186             if (this.form.progressUrl) {
11187                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11188                     (new Date() * 1) + '' + Math.random());
11189                     
11190             } 
11191             
11192             
11193             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11194                 form:this.form.el.dom,
11195                 url:this.getUrl(!isPost),
11196                 method: method,
11197                 params:isPost ? this.getParams() : null,
11198                 isUpload: this.form.fileUpload,
11199                 formData : this.form.formData
11200             }));
11201             
11202             this.uploadProgress();
11203
11204         }else if (o.clientValidation !== false){ // client validation failed
11205             this.failureType = Roo.form.Action.CLIENT_INVALID;
11206             this.form.afterAction(this, false);
11207         }
11208     },
11209
11210     success : function(response)
11211     {
11212         this.uploadComplete= true;
11213         if (this.haveProgress) {
11214             Roo.MessageBox.hide();
11215         }
11216         
11217         
11218         var result = this.processResponse(response);
11219         if(result === true || result.success){
11220             this.form.afterAction(this, true);
11221             return;
11222         }
11223         if(result.errors){
11224             this.form.markInvalid(result.errors);
11225             this.failureType = Roo.form.Action.SERVER_INVALID;
11226         }
11227         this.form.afterAction(this, false);
11228     },
11229     failure : function(response)
11230     {
11231         this.uploadComplete= true;
11232         if (this.haveProgress) {
11233             Roo.MessageBox.hide();
11234         }
11235         
11236         this.response = response;
11237         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11238         this.form.afterAction(this, false);
11239     },
11240     
11241     handleResponse : function(response){
11242         if(this.form.errorReader){
11243             var rs = this.form.errorReader.read(response);
11244             var errors = [];
11245             if(rs.records){
11246                 for(var i = 0, len = rs.records.length; i < len; i++) {
11247                     var r = rs.records[i];
11248                     errors[i] = r.data;
11249                 }
11250             }
11251             if(errors.length < 1){
11252                 errors = null;
11253             }
11254             return {
11255                 success : rs.success,
11256                 errors : errors
11257             };
11258         }
11259         var ret = false;
11260         try {
11261             ret = Roo.decode(response.responseText);
11262         } catch (e) {
11263             ret = {
11264                 success: false,
11265                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11266                 errors : []
11267             };
11268         }
11269         return ret;
11270         
11271     }
11272 });
11273
11274
11275 Roo.form.Action.Load = function(form, options){
11276     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11277     this.reader = this.form.reader;
11278 };
11279
11280 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11281     type : 'load',
11282
11283     run : function(){
11284         
11285         Roo.Ajax.request(Roo.apply(
11286                 this.createCallback(), {
11287                     method:this.getMethod(),
11288                     url:this.getUrl(false),
11289                     params:this.getParams()
11290         }));
11291     },
11292
11293     success : function(response){
11294         
11295         var result = this.processResponse(response);
11296         if(result === true || !result.success || !result.data){
11297             this.failureType = Roo.form.Action.LOAD_FAILURE;
11298             this.form.afterAction(this, false);
11299             return;
11300         }
11301         this.form.clearInvalid();
11302         this.form.setValues(result.data);
11303         this.form.afterAction(this, true);
11304     },
11305
11306     handleResponse : function(response){
11307         if(this.form.reader){
11308             var rs = this.form.reader.read(response);
11309             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11310             return {
11311                 success : rs.success,
11312                 data : data
11313             };
11314         }
11315         return Roo.decode(response.responseText);
11316     }
11317 });
11318
11319 Roo.form.Action.ACTION_TYPES = {
11320     'load' : Roo.form.Action.Load,
11321     'submit' : Roo.form.Action.Submit
11322 };/*
11323  * - LGPL
11324  *
11325  * form
11326  *
11327  */
11328
11329 /**
11330  * @class Roo.bootstrap.form.Form
11331  * @extends Roo.bootstrap.Component
11332  * @children Roo.bootstrap.Component
11333  * Bootstrap Form class
11334  * @cfg {String} method  GET | POST (default POST)
11335  * @cfg {String} labelAlign top | left (default top)
11336  * @cfg {String} align left  | right - for navbars
11337  * @cfg {Boolean} loadMask load mask when submit (default true)
11338
11339  *
11340  * @constructor
11341  * Create a new Form
11342  * @param {Object} config The config object
11343  */
11344
11345
11346 Roo.bootstrap.form.Form = function(config){
11347     
11348     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11349     
11350     Roo.bootstrap.form.Form.popover.apply();
11351     
11352     this.addEvents({
11353         /**
11354          * @event clientvalidation
11355          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11356          * @param {Form} this
11357          * @param {Boolean} valid true if the form has passed client-side validation
11358          */
11359         clientvalidation: true,
11360         /**
11361          * @event beforeaction
11362          * Fires before any action is performed. Return false to cancel the action.
11363          * @param {Form} this
11364          * @param {Action} action The action to be performed
11365          */
11366         beforeaction: true,
11367         /**
11368          * @event actionfailed
11369          * Fires when an action fails.
11370          * @param {Form} this
11371          * @param {Action} action The action that failed
11372          */
11373         actionfailed : true,
11374         /**
11375          * @event actioncomplete
11376          * Fires when an action is completed.
11377          * @param {Form} this
11378          * @param {Action} action The action that completed
11379          */
11380         actioncomplete : true
11381     });
11382 };
11383
11384 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11385
11386      /**
11387      * @cfg {String} method
11388      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11389      */
11390     method : 'POST',
11391     /**
11392      * @cfg {String} url
11393      * The URL to use for form actions if one isn't supplied in the action options.
11394      */
11395     /**
11396      * @cfg {Boolean} fileUpload
11397      * Set to true if this form is a file upload.
11398      */
11399
11400     /**
11401      * @cfg {Object} baseParams
11402      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11403      */
11404
11405     /**
11406      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11407      */
11408     timeout: 30,
11409     /**
11410      * @cfg {Sting} align (left|right) for navbar forms
11411      */
11412     align : 'left',
11413
11414     // private
11415     activeAction : null,
11416
11417     /**
11418      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11419      * element by passing it or its id or mask the form itself by passing in true.
11420      * @type Mixed
11421      */
11422     waitMsgTarget : false,
11423
11424     loadMask : true,
11425     
11426     /**
11427      * @cfg {Boolean} errorMask (true|false) default false
11428      */
11429     errorMask : false,
11430     
11431     /**
11432      * @cfg {Number} maskOffset Default 100
11433      */
11434     maskOffset : 100,
11435     
11436     /**
11437      * @cfg {Boolean} maskBody
11438      */
11439     maskBody : false,
11440
11441     getAutoCreate : function(){
11442
11443         var cfg = {
11444             tag: 'form',
11445             method : this.method || 'POST',
11446             id : this.id || Roo.id(),
11447             cls : ''
11448         };
11449         if (this.parent().xtype.match(/^Nav/)) {
11450             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11451
11452         }
11453
11454         if (this.labelAlign == 'left' ) {
11455             cfg.cls += ' form-horizontal';
11456         }
11457
11458
11459         return cfg;
11460     },
11461     initEvents : function()
11462     {
11463         this.el.on('submit', this.onSubmit, this);
11464         // this was added as random key presses on the form where triggering form submit.
11465         this.el.on('keypress', function(e) {
11466             if (e.getCharCode() != 13) {
11467                 return true;
11468             }
11469             // we might need to allow it for textareas.. and some other items.
11470             // check e.getTarget().
11471
11472             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11473                 return true;
11474             }
11475
11476             Roo.log("keypress blocked");
11477
11478             e.preventDefault();
11479             return false;
11480         });
11481         
11482     },
11483     // private
11484     onSubmit : function(e){
11485         e.stopEvent();
11486     },
11487
11488      /**
11489      * Returns true if client-side validation on the form is successful.
11490      * @return Boolean
11491      */
11492     isValid : function(){
11493         var items = this.getItems();
11494         var valid = true;
11495         var target = false;
11496         
11497         items.each(function(f){
11498             
11499             if(f.validate()){
11500                 return;
11501             }
11502             
11503             Roo.log('invalid field: ' + f.name);
11504             
11505             valid = false;
11506
11507             if(!target && f.el.isVisible(true)){
11508                 target = f;
11509             }
11510            
11511         });
11512         
11513         if(this.errorMask && !valid){
11514             Roo.bootstrap.form.Form.popover.mask(this, target);
11515         }
11516         
11517         return valid;
11518     },
11519     
11520     /**
11521      * Returns true if any fields in this form have changed since their original load.
11522      * @return Boolean
11523      */
11524     isDirty : function(){
11525         var dirty = false;
11526         var items = this.getItems();
11527         items.each(function(f){
11528            if(f.isDirty()){
11529                dirty = true;
11530                return false;
11531            }
11532            return true;
11533         });
11534         return dirty;
11535     },
11536      /**
11537      * Performs a predefined action (submit or load) or custom actions you define on this form.
11538      * @param {String} actionName The name of the action type
11539      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11540      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11541      * accept other config options):
11542      * <pre>
11543 Property          Type             Description
11544 ----------------  ---------------  ----------------------------------------------------------------------------------
11545 url               String           The url for the action (defaults to the form's url)
11546 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11547 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11548 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11549                                    validate the form on the client (defaults to false)
11550      * </pre>
11551      * @return {BasicForm} this
11552      */
11553     doAction : function(action, options){
11554         if(typeof action == 'string'){
11555             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11556         }
11557         if(this.fireEvent('beforeaction', this, action) !== false){
11558             this.beforeAction(action);
11559             action.run.defer(100, action);
11560         }
11561         return this;
11562     },
11563
11564     // private
11565     beforeAction : function(action){
11566         var o = action.options;
11567         
11568         if(this.loadMask){
11569             
11570             if(this.maskBody){
11571                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11572             } else {
11573                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11574             }
11575         }
11576         // not really supported yet.. ??
11577
11578         //if(this.waitMsgTarget === true){
11579         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11580         //}else if(this.waitMsgTarget){
11581         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11582         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11583         //}else {
11584         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11585        // }
11586
11587     },
11588
11589     // private
11590     afterAction : function(action, success){
11591         this.activeAction = null;
11592         var o = action.options;
11593
11594         if(this.loadMask){
11595             
11596             if(this.maskBody){
11597                 Roo.get(document.body).unmask();
11598             } else {
11599                 this.el.unmask();
11600             }
11601         }
11602         
11603         //if(this.waitMsgTarget === true){
11604 //            this.el.unmask();
11605         //}else if(this.waitMsgTarget){
11606         //    this.waitMsgTarget.unmask();
11607         //}else{
11608         //    Roo.MessageBox.updateProgress(1);
11609         //    Roo.MessageBox.hide();
11610        // }
11611         //
11612         if(success){
11613             if(o.reset){
11614                 this.reset();
11615             }
11616             Roo.callback(o.success, o.scope, [this, action]);
11617             this.fireEvent('actioncomplete', this, action);
11618
11619         }else{
11620
11621             // failure condition..
11622             // we have a scenario where updates need confirming.
11623             // eg. if a locking scenario exists..
11624             // we look for { errors : { needs_confirm : true }} in the response.
11625             if (
11626                 (typeof(action.result) != 'undefined')  &&
11627                 (typeof(action.result.errors) != 'undefined')  &&
11628                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11629            ){
11630                 var _t = this;
11631                 Roo.log("not supported yet");
11632                  /*
11633
11634                 Roo.MessageBox.confirm(
11635                     "Change requires confirmation",
11636                     action.result.errorMsg,
11637                     function(r) {
11638                         if (r != 'yes') {
11639                             return;
11640                         }
11641                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11642                     }
11643
11644                 );
11645                 */
11646
11647
11648                 return;
11649             }
11650
11651             Roo.callback(o.failure, o.scope, [this, action]);
11652             // show an error message if no failed handler is set..
11653             if (!this.hasListener('actionfailed')) {
11654                 Roo.log("need to add dialog support");
11655                 /*
11656                 Roo.MessageBox.alert("Error",
11657                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11658                         action.result.errorMsg :
11659                         "Saving Failed, please check your entries or try again"
11660                 );
11661                 */
11662             }
11663
11664             this.fireEvent('actionfailed', this, action);
11665         }
11666
11667     },
11668     /**
11669      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11670      * @param {String} id The value to search for
11671      * @return Field
11672      */
11673     findField : function(id){
11674         var items = this.getItems();
11675         var field = items.get(id);
11676         if(!field){
11677              items.each(function(f){
11678                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11679                     field = f;
11680                     return false;
11681                 }
11682                 return true;
11683             });
11684         }
11685         return field || null;
11686     },
11687      /**
11688      * Mark fields in this form invalid in bulk.
11689      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11690      * @return {BasicForm} this
11691      */
11692     markInvalid : function(errors){
11693         if(errors instanceof Array){
11694             for(var i = 0, len = errors.length; i < len; i++){
11695                 var fieldError = errors[i];
11696                 var f = this.findField(fieldError.id);
11697                 if(f){
11698                     f.markInvalid(fieldError.msg);
11699                 }
11700             }
11701         }else{
11702             var field, id;
11703             for(id in errors){
11704                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11705                     field.markInvalid(errors[id]);
11706                 }
11707             }
11708         }
11709         //Roo.each(this.childForms || [], function (f) {
11710         //    f.markInvalid(errors);
11711         //});
11712
11713         return this;
11714     },
11715
11716     /**
11717      * Set values for fields in this form in bulk.
11718      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11719      * @return {BasicForm} this
11720      */
11721     setValues : function(values){
11722         if(values instanceof Array){ // array of objects
11723             for(var i = 0, len = values.length; i < len; i++){
11724                 var v = values[i];
11725                 var f = this.findField(v.id);
11726                 if(f){
11727                     f.setValue(v.value);
11728                     if(this.trackResetOnLoad){
11729                         f.originalValue = f.getValue();
11730                     }
11731                 }
11732             }
11733         }else{ // object hash
11734             var field, id;
11735             for(id in values){
11736                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11737
11738                     if (field.setFromData &&
11739                         field.valueField &&
11740                         field.displayField &&
11741                         // combos' with local stores can
11742                         // be queried via setValue()
11743                         // to set their value..
11744                         (field.store && !field.store.isLocal)
11745                         ) {
11746                         // it's a combo
11747                         var sd = { };
11748                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11749                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11750                         field.setFromData(sd);
11751
11752                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11753                         
11754                         field.setFromData(values);
11755                         
11756                     } else {
11757                         field.setValue(values[id]);
11758                     }
11759
11760
11761                     if(this.trackResetOnLoad){
11762                         field.originalValue = field.getValue();
11763                     }
11764                 }
11765             }
11766         }
11767
11768         //Roo.each(this.childForms || [], function (f) {
11769         //    f.setValues(values);
11770         //});
11771
11772         return this;
11773     },
11774
11775     /**
11776      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11777      * they are returned as an array.
11778      * @param {Boolean} asString
11779      * @return {Object}
11780      */
11781     getValues : function(asString){
11782         //if (this.childForms) {
11783             // copy values from the child forms
11784         //    Roo.each(this.childForms, function (f) {
11785         //        this.setValues(f.getValues());
11786         //    }, this);
11787         //}
11788
11789
11790
11791         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11792         if(asString === true){
11793             return fs;
11794         }
11795         return Roo.urlDecode(fs);
11796     },
11797
11798     /**
11799      * Returns the fields in this form as an object with key/value pairs.
11800      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11801      * @return {Object}
11802      */
11803     getFieldValues : function(with_hidden)
11804     {
11805         var items = this.getItems();
11806         var ret = {};
11807         items.each(function(f){
11808             
11809             if (!f.getName()) {
11810                 return;
11811             }
11812             
11813             var v = f.getValue();
11814             
11815             if (f.inputType =='radio') {
11816                 if (typeof(ret[f.getName()]) == 'undefined') {
11817                     ret[f.getName()] = ''; // empty..
11818                 }
11819
11820                 if (!f.el.dom.checked) {
11821                     return;
11822
11823                 }
11824                 v = f.el.dom.value;
11825
11826             }
11827             
11828             if(f.xtype == 'MoneyField'){
11829                 ret[f.currencyName] = f.getCurrency();
11830             }
11831
11832             // not sure if this supported any more..
11833             if ((typeof(v) == 'object') && f.getRawValue) {
11834                 v = f.getRawValue() ; // dates..
11835             }
11836             // combo boxes where name != hiddenName...
11837             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11838                 ret[f.name] = f.getRawValue();
11839             }
11840             ret[f.getName()] = v;
11841         });
11842
11843         return ret;
11844     },
11845
11846     /**
11847      * Clears all invalid messages in this form.
11848      * @return {BasicForm} this
11849      */
11850     clearInvalid : function(){
11851         var items = this.getItems();
11852
11853         items.each(function(f){
11854            f.clearInvalid();
11855         });
11856
11857         return this;
11858     },
11859
11860     /**
11861      * Resets this form.
11862      * @return {BasicForm} this
11863      */
11864     reset : function(){
11865         var items = this.getItems();
11866         items.each(function(f){
11867             f.reset();
11868         });
11869
11870         Roo.each(this.childForms || [], function (f) {
11871             f.reset();
11872         });
11873
11874
11875         return this;
11876     },
11877     
11878     getItems : function()
11879     {
11880         var r=new Roo.util.MixedCollection(false, function(o){
11881             return o.id || (o.id = Roo.id());
11882         });
11883         var iter = function(el) {
11884             if (el.inputEl) {
11885                 r.add(el);
11886             }
11887             if (!el.items) {
11888                 return;
11889             }
11890             Roo.each(el.items,function(e) {
11891                 iter(e);
11892             });
11893         };
11894
11895         iter(this);
11896         return r;
11897     },
11898     
11899     hideFields : function(items)
11900     {
11901         Roo.each(items, function(i){
11902             
11903             var f = this.findField(i);
11904             
11905             if(!f){
11906                 return;
11907             }
11908             
11909             f.hide();
11910             
11911         }, this);
11912     },
11913     
11914     showFields : function(items)
11915     {
11916         Roo.each(items, function(i){
11917             
11918             var f = this.findField(i);
11919             
11920             if(!f){
11921                 return;
11922             }
11923             
11924             f.show();
11925             
11926         }, this);
11927     }
11928
11929 });
11930
11931 Roo.apply(Roo.bootstrap.form.Form, {
11932     
11933     popover : {
11934         
11935         padding : 5,
11936         
11937         isApplied : false,
11938         
11939         isMasked : false,
11940         
11941         form : false,
11942         
11943         target : false,
11944         
11945         toolTip : false,
11946         
11947         intervalID : false,
11948         
11949         maskEl : false,
11950         
11951         apply : function()
11952         {
11953             if(this.isApplied){
11954                 return;
11955             }
11956             
11957             this.maskEl = {
11958                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11959                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11960                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11961                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11962             };
11963             
11964             this.maskEl.top.enableDisplayMode("block");
11965             this.maskEl.left.enableDisplayMode("block");
11966             this.maskEl.bottom.enableDisplayMode("block");
11967             this.maskEl.right.enableDisplayMode("block");
11968             
11969             this.toolTip = new Roo.bootstrap.Tooltip({
11970                 cls : 'roo-form-error-popover',
11971                 alignment : {
11972                     'left' : ['r-l', [-2,0], 'right'],
11973                     'right' : ['l-r', [2,0], 'left'],
11974                     'bottom' : ['tl-bl', [0,2], 'top'],
11975                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11976                 }
11977             });
11978             
11979             this.toolTip.render(Roo.get(document.body));
11980
11981             this.toolTip.el.enableDisplayMode("block");
11982             
11983             Roo.get(document.body).on('click', function(){
11984                 this.unmask();
11985             }, this);
11986             
11987             Roo.get(document.body).on('touchstart', function(){
11988                 this.unmask();
11989             }, this);
11990             
11991             this.isApplied = true
11992         },
11993         
11994         mask : function(form, target)
11995         {
11996             this.form = form;
11997             
11998             this.target = target;
11999             
12000             if(!this.form.errorMask || !target.el){
12001                 return;
12002             }
12003             
12004             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12005             
12006             Roo.log(scrollable);
12007             
12008             var ot = this.target.el.calcOffsetsTo(scrollable);
12009             
12010             var scrollTo = ot[1] - this.form.maskOffset;
12011             
12012             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12013             
12014             scrollable.scrollTo('top', scrollTo);
12015             
12016             var box = this.target.el.getBox();
12017             Roo.log(box);
12018             var zIndex = Roo.bootstrap.Modal.zIndex++;
12019
12020             
12021             this.maskEl.top.setStyle('position', 'absolute');
12022             this.maskEl.top.setStyle('z-index', zIndex);
12023             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12024             this.maskEl.top.setLeft(0);
12025             this.maskEl.top.setTop(0);
12026             this.maskEl.top.show();
12027             
12028             this.maskEl.left.setStyle('position', 'absolute');
12029             this.maskEl.left.setStyle('z-index', zIndex);
12030             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12031             this.maskEl.left.setLeft(0);
12032             this.maskEl.left.setTop(box.y - this.padding);
12033             this.maskEl.left.show();
12034
12035             this.maskEl.bottom.setStyle('position', 'absolute');
12036             this.maskEl.bottom.setStyle('z-index', zIndex);
12037             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12038             this.maskEl.bottom.setLeft(0);
12039             this.maskEl.bottom.setTop(box.bottom + this.padding);
12040             this.maskEl.bottom.show();
12041
12042             this.maskEl.right.setStyle('position', 'absolute');
12043             this.maskEl.right.setStyle('z-index', zIndex);
12044             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12045             this.maskEl.right.setLeft(box.right + this.padding);
12046             this.maskEl.right.setTop(box.y - this.padding);
12047             this.maskEl.right.show();
12048
12049             this.toolTip.bindEl = this.target.el;
12050
12051             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12052
12053             var tip = this.target.blankText;
12054
12055             if(this.target.getValue() !== '' ) {
12056                 
12057                 if (this.target.invalidText.length) {
12058                     tip = this.target.invalidText;
12059                 } else if (this.target.regexText.length){
12060                     tip = this.target.regexText;
12061                 }
12062             }
12063
12064             this.toolTip.show(tip);
12065
12066             this.intervalID = window.setInterval(function() {
12067                 Roo.bootstrap.form.Form.popover.unmask();
12068             }, 10000);
12069
12070             window.onwheel = function(){ return false;};
12071             
12072             (function(){ this.isMasked = true; }).defer(500, this);
12073             
12074         },
12075         
12076         unmask : function()
12077         {
12078             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12079                 return;
12080             }
12081             
12082             this.maskEl.top.setStyle('position', 'absolute');
12083             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12084             this.maskEl.top.hide();
12085
12086             this.maskEl.left.setStyle('position', 'absolute');
12087             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12088             this.maskEl.left.hide();
12089
12090             this.maskEl.bottom.setStyle('position', 'absolute');
12091             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12092             this.maskEl.bottom.hide();
12093
12094             this.maskEl.right.setStyle('position', 'absolute');
12095             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12096             this.maskEl.right.hide();
12097             
12098             this.toolTip.hide();
12099             
12100             this.toolTip.el.hide();
12101             
12102             window.onwheel = function(){ return true;};
12103             
12104             if(this.intervalID){
12105                 window.clearInterval(this.intervalID);
12106                 this.intervalID = false;
12107             }
12108             
12109             this.isMasked = false;
12110             
12111         }
12112         
12113     }
12114     
12115 });
12116
12117 /*
12118  * Based on:
12119  * Ext JS Library 1.1.1
12120  * Copyright(c) 2006-2007, Ext JS, LLC.
12121  *
12122  * Originally Released Under LGPL - original licence link has changed is not relivant.
12123  *
12124  * Fork - LGPL
12125  * <script type="text/javascript">
12126  */
12127 /**
12128  * @class Roo.form.VTypes
12129  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12130  * @static
12131  */
12132 Roo.form.VTypes = function(){
12133     // closure these in so they are only created once.
12134     var alpha = /^[a-zA-Z_]+$/;
12135     var alphanum = /^[a-zA-Z0-9_]+$/;
12136     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12137     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12138
12139     // All these messages and functions are configurable
12140     return {
12141         /**
12142          * The function used to validate email addresses
12143          * @param {String} value The email address
12144          */
12145         'email' : function(v){
12146             return email.test(v);
12147         },
12148         /**
12149          * The error text to display when the email validation function returns false
12150          * @type String
12151          */
12152         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12153         /**
12154          * The keystroke filter mask to be applied on email input
12155          * @type RegExp
12156          */
12157         'emailMask' : /[a-z0-9_\.\-@]/i,
12158
12159         /**
12160          * The function used to validate URLs
12161          * @param {String} value The URL
12162          */
12163         'url' : function(v){
12164             return url.test(v);
12165         },
12166         /**
12167          * The error text to display when the url validation function returns false
12168          * @type String
12169          */
12170         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12171         
12172         /**
12173          * The function used to validate alpha values
12174          * @param {String} value The value
12175          */
12176         'alpha' : function(v){
12177             return alpha.test(v);
12178         },
12179         /**
12180          * The error text to display when the alpha validation function returns false
12181          * @type String
12182          */
12183         'alphaText' : 'This field should only contain letters and _',
12184         /**
12185          * The keystroke filter mask to be applied on alpha input
12186          * @type RegExp
12187          */
12188         'alphaMask' : /[a-z_]/i,
12189
12190         /**
12191          * The function used to validate alphanumeric values
12192          * @param {String} value The value
12193          */
12194         'alphanum' : function(v){
12195             return alphanum.test(v);
12196         },
12197         /**
12198          * The error text to display when the alphanumeric validation function returns false
12199          * @type String
12200          */
12201         'alphanumText' : 'This field should only contain letters, numbers and _',
12202         /**
12203          * The keystroke filter mask to be applied on alphanumeric input
12204          * @type RegExp
12205          */
12206         'alphanumMask' : /[a-z0-9_]/i
12207     };
12208 }();/*
12209  * - LGPL
12210  *
12211  * Input
12212  * 
12213  */
12214
12215 /**
12216  * @class Roo.bootstrap.form.Input
12217  * @extends Roo.bootstrap.Component
12218  * Bootstrap Input class
12219  * @cfg {Boolean} disabled is it disabled
12220  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12221  * @cfg {String} name name of the input
12222  * @cfg {string} fieldLabel - the label associated
12223  * @cfg {string} placeholder - placeholder to put in text.
12224  * @cfg {string} before - input group add on before
12225  * @cfg {string} after - input group add on after
12226  * @cfg {string} size - (lg|sm) or leave empty..
12227  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12228  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12229  * @cfg {Number} md colspan out of 12 for computer-sized screens
12230  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12231  * @cfg {string} value default value of the input
12232  * @cfg {Number} labelWidth set the width of label 
12233  * @cfg {Number} labellg set the width of label (1-12)
12234  * @cfg {Number} labelmd set the width of label (1-12)
12235  * @cfg {Number} labelsm set the width of label (1-12)
12236  * @cfg {Number} labelxs set the width of label (1-12)
12237  * @cfg {String} labelAlign (top|left)
12238  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12239  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12240  * @cfg {String} indicatorpos (left|right) default left
12241  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12242  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12243  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12244  * @cfg {Roo.bootstrap.Button} before Button to show before
12245  * @cfg {Roo.bootstrap.Button} afterButton to show before
12246  * @cfg {String} align (left|center|right) Default left
12247  * @cfg {Boolean} forceFeedback (true|false) Default false
12248  * 
12249  * @constructor
12250  * Create a new Input
12251  * @param {Object} config The config object
12252  */
12253
12254 Roo.bootstrap.form.Input = function(config){
12255     
12256     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12257     
12258     this.addEvents({
12259         /**
12260          * @event focus
12261          * Fires when this field receives input focus.
12262          * @param {Roo.form.Field} this
12263          */
12264         focus : true,
12265         /**
12266          * @event blur
12267          * Fires when this field loses input focus.
12268          * @param {Roo.form.Field} this
12269          */
12270         blur : true,
12271         /**
12272          * @event specialkey
12273          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12274          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12275          * @param {Roo.form.Field} this
12276          * @param {Roo.EventObject} e The event object
12277          */
12278         specialkey : true,
12279         /**
12280          * @event change
12281          * Fires just before the field blurs if the field value has changed.
12282          * @param {Roo.form.Field} this
12283          * @param {Mixed} newValue The new value
12284          * @param {Mixed} oldValue The original value
12285          */
12286         change : true,
12287         /**
12288          * @event invalid
12289          * Fires after the field has been marked as invalid.
12290          * @param {Roo.form.Field} this
12291          * @param {String} msg The validation message
12292          */
12293         invalid : true,
12294         /**
12295          * @event valid
12296          * Fires after the field has been validated with no errors.
12297          * @param {Roo.form.Field} this
12298          */
12299         valid : true,
12300          /**
12301          * @event keyup
12302          * Fires after the key up
12303          * @param {Roo.form.Field} this
12304          * @param {Roo.EventObject}  e The event Object
12305          */
12306         keyup : true,
12307         /**
12308          * @event paste
12309          * Fires after the user pastes into input
12310          * @param {Roo.form.Field} this
12311          * @param {Roo.EventObject}  e The event Object
12312          */
12313         paste : true
12314     });
12315 };
12316
12317 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12318      /**
12319      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12320       automatic validation (defaults to "keyup").
12321      */
12322     validationEvent : "keyup",
12323      /**
12324      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12325      */
12326     validateOnBlur : true,
12327     /**
12328      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12329      */
12330     validationDelay : 250,
12331      /**
12332      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12333      */
12334     focusClass : "x-form-focus",  // not needed???
12335     
12336        
12337     /**
12338      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12339      */
12340     invalidClass : "has-warning",
12341     
12342     /**
12343      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12344      */
12345     validClass : "has-success",
12346     
12347     /**
12348      * @cfg {Boolean} hasFeedback (true|false) default true
12349      */
12350     hasFeedback : true,
12351     
12352     /**
12353      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12354      */
12355     invalidFeedbackClass : "glyphicon-warning-sign",
12356     
12357     /**
12358      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12359      */
12360     validFeedbackClass : "glyphicon-ok",
12361     
12362     /**
12363      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12364      */
12365     selectOnFocus : false,
12366     
12367      /**
12368      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12369      */
12370     maskRe : null,
12371        /**
12372      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12373      */
12374     vtype : null,
12375     
12376       /**
12377      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12378      */
12379     disableKeyFilter : false,
12380     
12381        /**
12382      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12383      */
12384     disabled : false,
12385      /**
12386      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12387      */
12388     allowBlank : true,
12389     /**
12390      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12391      */
12392     blankText : "Please complete this mandatory field",
12393     
12394      /**
12395      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12396      */
12397     minLength : 0,
12398     /**
12399      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12400      */
12401     maxLength : Number.MAX_VALUE,
12402     /**
12403      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12404      */
12405     minLengthText : "The minimum length for this field is {0}",
12406     /**
12407      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12408      */
12409     maxLengthText : "The maximum length for this field is {0}",
12410   
12411     
12412     /**
12413      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12414      * If available, this function will be called only after the basic validators all return true, and will be passed the
12415      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12416      */
12417     validator : null,
12418     /**
12419      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12420      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12421      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12422      */
12423     regex : null,
12424     /**
12425      * @cfg {String} regexText -- Depricated - use Invalid Text
12426      */
12427     regexText : "",
12428     
12429     /**
12430      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12431      */
12432     invalidText : "",
12433     
12434     
12435     
12436     autocomplete: false,
12437     
12438     
12439     fieldLabel : '',
12440     inputType : 'text',
12441     
12442     name : false,
12443     placeholder: false,
12444     before : false,
12445     after : false,
12446     size : false,
12447     hasFocus : false,
12448     preventMark: false,
12449     isFormField : true,
12450     value : '',
12451     labelWidth : 2,
12452     labelAlign : false,
12453     readOnly : false,
12454     align : false,
12455     formatedValue : false,
12456     forceFeedback : false,
12457     
12458     indicatorpos : 'left',
12459     
12460     labellg : 0,
12461     labelmd : 0,
12462     labelsm : 0,
12463     labelxs : 0,
12464     
12465     capture : '',
12466     accept : '',
12467     
12468     parentLabelAlign : function()
12469     {
12470         var parent = this;
12471         while (parent.parent()) {
12472             parent = parent.parent();
12473             if (typeof(parent.labelAlign) !='undefined') {
12474                 return parent.labelAlign;
12475             }
12476         }
12477         return 'left';
12478         
12479     },
12480     
12481     getAutoCreate : function()
12482     {
12483         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12484         
12485         var id = Roo.id();
12486         
12487         var cfg = {};
12488         
12489         if(this.inputType != 'hidden'){
12490             cfg.cls = 'form-group' //input-group
12491         }
12492         
12493         var input =  {
12494             tag: 'input',
12495             id : id,
12496             type : this.inputType,
12497             value : this.value,
12498             cls : 'form-control',
12499             placeholder : this.placeholder || '',
12500             autocomplete : this.autocomplete || 'new-password'
12501         };
12502         if (this.inputType == 'file') {
12503             input.style = 'overflow:hidden'; // why not in CSS?
12504         }
12505         
12506         if(this.capture.length){
12507             input.capture = this.capture;
12508         }
12509         
12510         if(this.accept.length){
12511             input.accept = this.accept + "/*";
12512         }
12513         
12514         if(this.align){
12515             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12516         }
12517         
12518         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12519             input.maxLength = this.maxLength;
12520         }
12521         
12522         if (this.disabled) {
12523             input.disabled=true;
12524         }
12525         
12526         if (this.readOnly) {
12527             input.readonly=true;
12528         }
12529         
12530         if (this.name) {
12531             input.name = this.name;
12532         }
12533         
12534         if (this.size) {
12535             input.cls += ' input-' + this.size;
12536         }
12537         
12538         var settings=this;
12539         ['xs','sm','md','lg'].map(function(size){
12540             if (settings[size]) {
12541                 cfg.cls += ' col-' + size + '-' + settings[size];
12542             }
12543         });
12544         
12545         var inputblock = input;
12546         
12547         var feedback = {
12548             tag: 'span',
12549             cls: 'glyphicon form-control-feedback'
12550         };
12551             
12552         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12553             
12554             inputblock = {
12555                 cls : 'has-feedback',
12556                 cn :  [
12557                     input,
12558                     feedback
12559                 ] 
12560             };  
12561         }
12562         
12563         if (this.before || this.after) {
12564             
12565             inputblock = {
12566                 cls : 'input-group',
12567                 cn :  [] 
12568             };
12569             
12570             if (this.before && typeof(this.before) == 'string') {
12571                 
12572                 inputblock.cn.push({
12573                     tag :'span',
12574                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12575                     html : this.before
12576                 });
12577             }
12578             if (this.before && typeof(this.before) == 'object') {
12579                 this.before = Roo.factory(this.before);
12580                 
12581                 inputblock.cn.push({
12582                     tag :'span',
12583                     cls : 'roo-input-before input-group-prepend   input-group-' +
12584                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12585                 });
12586             }
12587             
12588             inputblock.cn.push(input);
12589             
12590             if (this.after && typeof(this.after) == 'string') {
12591                 inputblock.cn.push({
12592                     tag :'span',
12593                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12594                     html : this.after
12595                 });
12596             }
12597             if (this.after && typeof(this.after) == 'object') {
12598                 this.after = Roo.factory(this.after);
12599                 
12600                 inputblock.cn.push({
12601                     tag :'span',
12602                     cls : 'roo-input-after input-group-append  input-group-' +
12603                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12604                 });
12605             }
12606             
12607             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12608                 inputblock.cls += ' has-feedback';
12609                 inputblock.cn.push(feedback);
12610             }
12611         };
12612         var indicator = {
12613             tag : 'i',
12614             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12615             tooltip : 'This field is required'
12616         };
12617         if (this.allowBlank ) {
12618             indicator.style = this.allowBlank ? ' display:none' : '';
12619         }
12620         if (align ==='left' && this.fieldLabel.length) {
12621             
12622             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12623             
12624             cfg.cn = [
12625                 indicator,
12626                 {
12627                     tag: 'label',
12628                     'for' :  id,
12629                     cls : 'control-label col-form-label',
12630                     html : this.fieldLabel
12631
12632                 },
12633                 {
12634                     cls : "", 
12635                     cn: [
12636                         inputblock
12637                     ]
12638                 }
12639             ];
12640             
12641             var labelCfg = cfg.cn[1];
12642             var contentCfg = cfg.cn[2];
12643             
12644             if(this.indicatorpos == 'right'){
12645                 cfg.cn = [
12646                     {
12647                         tag: 'label',
12648                         'for' :  id,
12649                         cls : 'control-label col-form-label',
12650                         cn : [
12651                             {
12652                                 tag : 'span',
12653                                 html : this.fieldLabel
12654                             },
12655                             indicator
12656                         ]
12657                     },
12658                     {
12659                         cls : "",
12660                         cn: [
12661                             inputblock
12662                         ]
12663                     }
12664
12665                 ];
12666                 
12667                 labelCfg = cfg.cn[0];
12668                 contentCfg = cfg.cn[1];
12669             
12670             }
12671             
12672             if(this.labelWidth > 12){
12673                 labelCfg.style = "width: " + this.labelWidth + 'px';
12674             }
12675             
12676             if(this.labelWidth < 13 && this.labelmd == 0){
12677                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12678             }
12679             
12680             if(this.labellg > 0){
12681                 labelCfg.cls += ' col-lg-' + this.labellg;
12682                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12683             }
12684             
12685             if(this.labelmd > 0){
12686                 labelCfg.cls += ' col-md-' + this.labelmd;
12687                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12688             }
12689             
12690             if(this.labelsm > 0){
12691                 labelCfg.cls += ' col-sm-' + this.labelsm;
12692                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12693             }
12694             
12695             if(this.labelxs > 0){
12696                 labelCfg.cls += ' col-xs-' + this.labelxs;
12697                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12698             }
12699             
12700             
12701         } else if ( this.fieldLabel.length) {
12702                 
12703             
12704             
12705             cfg.cn = [
12706                 {
12707                     tag : 'i',
12708                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12709                     tooltip : 'This field is required',
12710                     style : this.allowBlank ? ' display:none' : '' 
12711                 },
12712                 {
12713                     tag: 'label',
12714                    //cls : 'input-group-addon',
12715                     html : this.fieldLabel
12716
12717                 },
12718
12719                inputblock
12720
12721            ];
12722            
12723            if(this.indicatorpos == 'right'){
12724        
12725                 cfg.cn = [
12726                     {
12727                         tag: 'label',
12728                        //cls : 'input-group-addon',
12729                         html : this.fieldLabel
12730
12731                     },
12732                     {
12733                         tag : 'i',
12734                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12735                         tooltip : 'This field is required',
12736                         style : this.allowBlank ? ' display:none' : '' 
12737                     },
12738
12739                    inputblock
12740
12741                ];
12742
12743             }
12744
12745         } else {
12746             
12747             cfg.cn = [
12748
12749                     inputblock
12750
12751             ];
12752                 
12753                 
12754         };
12755         
12756         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12757            cfg.cls += ' navbar-form';
12758         }
12759         
12760         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12761             // on BS4 we do this only if not form 
12762             cfg.cls += ' navbar-form';
12763             cfg.tag = 'li';
12764         }
12765         
12766         return cfg;
12767         
12768     },
12769     /**
12770      * return the real input element.
12771      */
12772     inputEl: function ()
12773     {
12774         return this.el.select('input.form-control',true).first();
12775     },
12776     
12777     tooltipEl : function()
12778     {
12779         return this.inputEl();
12780     },
12781     
12782     indicatorEl : function()
12783     {
12784         if (Roo.bootstrap.version == 4) {
12785             return false; // not enabled in v4 yet.
12786         }
12787         
12788         var indicator = this.el.select('i.roo-required-indicator',true).first();
12789         
12790         if(!indicator){
12791             return false;
12792         }
12793         
12794         return indicator;
12795         
12796     },
12797     
12798     setDisabled : function(v)
12799     {
12800         var i  = this.inputEl().dom;
12801         if (!v) {
12802             i.removeAttribute('disabled');
12803             return;
12804             
12805         }
12806         i.setAttribute('disabled','true');
12807     },
12808     initEvents : function()
12809     {
12810           
12811         this.inputEl().on("keydown" , this.fireKey,  this);
12812         this.inputEl().on("focus", this.onFocus,  this);
12813         this.inputEl().on("blur", this.onBlur,  this);
12814         
12815         this.inputEl().relayEvent('keyup', this);
12816         this.inputEl().relayEvent('paste', this);
12817         
12818         this.indicator = this.indicatorEl();
12819         
12820         if(this.indicator){
12821             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12822         }
12823  
12824         // reference to original value for reset
12825         this.originalValue = this.getValue();
12826         //Roo.form.TextField.superclass.initEvents.call(this);
12827         if(this.validationEvent == 'keyup'){
12828             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12829             this.inputEl().on('keyup', this.filterValidation, this);
12830         }
12831         else if(this.validationEvent !== false){
12832             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12833         }
12834         
12835         if(this.selectOnFocus){
12836             this.on("focus", this.preFocus, this);
12837             
12838         }
12839         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12840             this.inputEl().on("keypress", this.filterKeys, this);
12841         } else {
12842             this.inputEl().relayEvent('keypress', this);
12843         }
12844        /* if(this.grow){
12845             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12846             this.el.on("click", this.autoSize,  this);
12847         }
12848         */
12849         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12850             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12851         }
12852         
12853         if (typeof(this.before) == 'object') {
12854             this.before.render(this.el.select('.roo-input-before',true).first());
12855         }
12856         if (typeof(this.after) == 'object') {
12857             this.after.render(this.el.select('.roo-input-after',true).first());
12858         }
12859         
12860         this.inputEl().on('change', this.onChange, this);
12861         
12862     },
12863     filterValidation : function(e){
12864         if(!e.isNavKeyPress()){
12865             this.validationTask.delay(this.validationDelay);
12866         }
12867     },
12868      /**
12869      * Validates the field value
12870      * @return {Boolean} True if the value is valid, else false
12871      */
12872     validate : function(){
12873         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12874         if(this.disabled || this.validateValue(this.getRawValue())){
12875             this.markValid();
12876             return true;
12877         }
12878         
12879         this.markInvalid();
12880         return false;
12881     },
12882     
12883     
12884     /**
12885      * Validates a value according to the field's validation rules and marks the field as invalid
12886      * if the validation fails
12887      * @param {Mixed} value The value to validate
12888      * @return {Boolean} True if the value is valid, else false
12889      */
12890     validateValue : function(value)
12891     {
12892         if(this.getVisibilityEl().hasClass('hidden')){
12893             return true;
12894         }
12895         
12896         if(value.length < 1)  { // if it's blank
12897             if(this.allowBlank){
12898                 return true;
12899             }
12900             return false;
12901         }
12902         
12903         if(value.length < this.minLength){
12904             return false;
12905         }
12906         if(value.length > this.maxLength){
12907             return false;
12908         }
12909         if(this.vtype){
12910             var vt = Roo.form.VTypes;
12911             if(!vt[this.vtype](value, this)){
12912                 return false;
12913             }
12914         }
12915         if(typeof this.validator == "function"){
12916             var msg = this.validator(value);
12917             if(msg !== true){
12918                 return false;
12919             }
12920             if (typeof(msg) == 'string') {
12921                 this.invalidText = msg;
12922             }
12923         }
12924         
12925         if(this.regex && !this.regex.test(value)){
12926             return false;
12927         }
12928         
12929         return true;
12930     },
12931     
12932      // private
12933     fireKey : function(e){
12934         //Roo.log('field ' + e.getKey());
12935         if(e.isNavKeyPress()){
12936             this.fireEvent("specialkey", this, e);
12937         }
12938     },
12939     focus : function (selectText){
12940         if(this.rendered){
12941             this.inputEl().focus();
12942             if(selectText === true){
12943                 this.inputEl().dom.select();
12944             }
12945         }
12946         return this;
12947     } ,
12948     
12949     onFocus : function(){
12950         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12951            // this.el.addClass(this.focusClass);
12952         }
12953         if(!this.hasFocus){
12954             this.hasFocus = true;
12955             this.startValue = this.getValue();
12956             this.fireEvent("focus", this);
12957         }
12958     },
12959     
12960     beforeBlur : Roo.emptyFn,
12961
12962     
12963     // private
12964     onBlur : function(){
12965         this.beforeBlur();
12966         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12967             //this.el.removeClass(this.focusClass);
12968         }
12969         this.hasFocus = false;
12970         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12971             this.validate();
12972         }
12973         var v = this.getValue();
12974         if(String(v) !== String(this.startValue)){
12975             this.fireEvent('change', this, v, this.startValue);
12976         }
12977         this.fireEvent("blur", this);
12978     },
12979     
12980     onChange : function(e)
12981     {
12982         var v = this.getValue();
12983         if(String(v) !== String(this.startValue)){
12984             this.fireEvent('change', this, v, this.startValue);
12985         }
12986         
12987     },
12988     
12989     /**
12990      * Resets the current field value to the originally loaded value and clears any validation messages
12991      */
12992     reset : function(){
12993         this.setValue(this.originalValue);
12994         this.validate();
12995     },
12996      /**
12997      * Returns the name of the field
12998      * @return {Mixed} name The name field
12999      */
13000     getName: function(){
13001         return this.name;
13002     },
13003      /**
13004      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13005      * @return {Mixed} value The field value
13006      */
13007     getValue : function(){
13008         
13009         var v = this.inputEl().getValue();
13010         
13011         return v;
13012     },
13013     /**
13014      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13015      * @return {Mixed} value The field value
13016      */
13017     getRawValue : function(){
13018         var v = this.inputEl().getValue();
13019         
13020         return v;
13021     },
13022     
13023     /**
13024      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13025      * @param {Mixed} value The value to set
13026      */
13027     setRawValue : function(v){
13028         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13029     },
13030     
13031     selectText : function(start, end){
13032         var v = this.getRawValue();
13033         if(v.length > 0){
13034             start = start === undefined ? 0 : start;
13035             end = end === undefined ? v.length : end;
13036             var d = this.inputEl().dom;
13037             if(d.setSelectionRange){
13038                 d.setSelectionRange(start, end);
13039             }else if(d.createTextRange){
13040                 var range = d.createTextRange();
13041                 range.moveStart("character", start);
13042                 range.moveEnd("character", v.length-end);
13043                 range.select();
13044             }
13045         }
13046     },
13047     
13048     /**
13049      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13050      * @param {Mixed} value The value to set
13051      */
13052     setValue : function(v){
13053         this.value = v;
13054         if(this.rendered){
13055             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13056             this.validate();
13057         }
13058     },
13059     
13060     /*
13061     processValue : function(value){
13062         if(this.stripCharsRe){
13063             var newValue = value.replace(this.stripCharsRe, '');
13064             if(newValue !== value){
13065                 this.setRawValue(newValue);
13066                 return newValue;
13067             }
13068         }
13069         return value;
13070     },
13071   */
13072     preFocus : function(){
13073         
13074         if(this.selectOnFocus){
13075             this.inputEl().dom.select();
13076         }
13077     },
13078     filterKeys : function(e){
13079         var k = e.getKey();
13080         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13081             return;
13082         }
13083         var c = e.getCharCode(), cc = String.fromCharCode(c);
13084         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13085             return;
13086         }
13087         if(!this.maskRe.test(cc)){
13088             e.stopEvent();
13089         }
13090     },
13091      /**
13092      * Clear any invalid styles/messages for this field
13093      */
13094     clearInvalid : function(){
13095         
13096         if(!this.el || this.preventMark){ // not rendered
13097             return;
13098         }
13099         
13100         
13101         this.el.removeClass([this.invalidClass, 'is-invalid']);
13102         
13103         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13104             
13105             var feedback = this.el.select('.form-control-feedback', true).first();
13106             
13107             if(feedback){
13108                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13109             }
13110             
13111         }
13112         
13113         if(this.indicator){
13114             this.indicator.removeClass('visible');
13115             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13116         }
13117         
13118         this.fireEvent('valid', this);
13119     },
13120     
13121      /**
13122      * Mark this field as valid
13123      */
13124     markValid : function()
13125     {
13126         if(!this.el  || this.preventMark){ // not rendered...
13127             return;
13128         }
13129         
13130         this.el.removeClass([this.invalidClass, this.validClass]);
13131         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13132
13133         var feedback = this.el.select('.form-control-feedback', true).first();
13134             
13135         if(feedback){
13136             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13137         }
13138         
13139         if(this.indicator){
13140             this.indicator.removeClass('visible');
13141             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13142         }
13143         
13144         if(this.disabled){
13145             return;
13146         }
13147         
13148            
13149         if(this.allowBlank && !this.getRawValue().length){
13150             return;
13151         }
13152         if (Roo.bootstrap.version == 3) {
13153             this.el.addClass(this.validClass);
13154         } else {
13155             this.inputEl().addClass('is-valid');
13156         }
13157
13158         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13159             
13160             var feedback = this.el.select('.form-control-feedback', true).first();
13161             
13162             if(feedback){
13163                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13164                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13165             }
13166             
13167         }
13168         
13169         this.fireEvent('valid', this);
13170     },
13171     
13172      /**
13173      * Mark this field as invalid
13174      * @param {String} msg The validation message
13175      */
13176     markInvalid : function(msg)
13177     {
13178         if(!this.el  || this.preventMark){ // not rendered
13179             return;
13180         }
13181         
13182         this.el.removeClass([this.invalidClass, this.validClass]);
13183         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13184         
13185         var feedback = this.el.select('.form-control-feedback', true).first();
13186             
13187         if(feedback){
13188             this.el.select('.form-control-feedback', true).first().removeClass(
13189                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13190         }
13191
13192         if(this.disabled){
13193             return;
13194         }
13195         
13196         if(this.allowBlank && !this.getRawValue().length){
13197             return;
13198         }
13199         
13200         if(this.indicator){
13201             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13202             this.indicator.addClass('visible');
13203         }
13204         if (Roo.bootstrap.version == 3) {
13205             this.el.addClass(this.invalidClass);
13206         } else {
13207             this.inputEl().addClass('is-invalid');
13208         }
13209         
13210         
13211         
13212         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13213             
13214             var feedback = this.el.select('.form-control-feedback', true).first();
13215             
13216             if(feedback){
13217                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13218                 
13219                 if(this.getValue().length || this.forceFeedback){
13220                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13221                 }
13222                 
13223             }
13224             
13225         }
13226         
13227         this.fireEvent('invalid', this, msg);
13228     },
13229     // private
13230     SafariOnKeyDown : function(event)
13231     {
13232         // this is a workaround for a password hang bug on chrome/ webkit.
13233         if (this.inputEl().dom.type != 'password') {
13234             return;
13235         }
13236         
13237         var isSelectAll = false;
13238         
13239         if(this.inputEl().dom.selectionEnd > 0){
13240             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13241         }
13242         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13243             event.preventDefault();
13244             this.setValue('');
13245             return;
13246         }
13247         
13248         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13249             
13250             event.preventDefault();
13251             // this is very hacky as keydown always get's upper case.
13252             //
13253             var cc = String.fromCharCode(event.getCharCode());
13254             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13255             
13256         }
13257     },
13258     adjustWidth : function(tag, w){
13259         tag = tag.toLowerCase();
13260         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13261             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13262                 if(tag == 'input'){
13263                     return w + 2;
13264                 }
13265                 if(tag == 'textarea'){
13266                     return w-2;
13267                 }
13268             }else if(Roo.isOpera){
13269                 if(tag == 'input'){
13270                     return w + 2;
13271                 }
13272                 if(tag == 'textarea'){
13273                     return w-2;
13274                 }
13275             }
13276         }
13277         return w;
13278     },
13279     
13280     setFieldLabel : function(v)
13281     {
13282         if(!this.rendered){
13283             return;
13284         }
13285         
13286         if(this.indicatorEl()){
13287             var ar = this.el.select('label > span',true);
13288             
13289             if (ar.elements.length) {
13290                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13291                 this.fieldLabel = v;
13292                 return;
13293             }
13294             
13295             var br = this.el.select('label',true);
13296             
13297             if(br.elements.length) {
13298                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13299                 this.fieldLabel = v;
13300                 return;
13301             }
13302             
13303             Roo.log('Cannot Found any of label > span || label in input');
13304             return;
13305         }
13306         
13307         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13308         this.fieldLabel = v;
13309         
13310         
13311     }
13312 });
13313
13314  
13315 /*
13316  * - LGPL
13317  *
13318  * Input
13319  * 
13320  */
13321
13322 /**
13323  * @class Roo.bootstrap.form.TextArea
13324  * @extends Roo.bootstrap.form.Input
13325  * Bootstrap TextArea class
13326  * @cfg {Number} cols Specifies the visible width of a text area
13327  * @cfg {Number} rows Specifies the visible number of lines in a text area
13328  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13329  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13330  * @cfg {string} html text
13331  * 
13332  * @constructor
13333  * Create a new TextArea
13334  * @param {Object} config The config object
13335  */
13336
13337 Roo.bootstrap.form.TextArea = function(config){
13338     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13339    
13340 };
13341
13342 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13343      
13344     cols : false,
13345     rows : 5,
13346     readOnly : false,
13347     warp : 'soft',
13348     resize : false,
13349     value: false,
13350     html: false,
13351     
13352     getAutoCreate : function(){
13353         
13354         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13355         
13356         var id = Roo.id();
13357         
13358         var cfg = {};
13359         
13360         if(this.inputType != 'hidden'){
13361             cfg.cls = 'form-group' //input-group
13362         }
13363         
13364         var input =  {
13365             tag: 'textarea',
13366             id : id,
13367             warp : this.warp,
13368             rows : this.rows,
13369             value : this.value || '',
13370             html: this.html || '',
13371             cls : 'form-control',
13372             placeholder : this.placeholder || '' 
13373             
13374         };
13375         
13376         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13377             input.maxLength = this.maxLength;
13378         }
13379         
13380         if(this.resize){
13381             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13382         }
13383         
13384         if(this.cols){
13385             input.cols = this.cols;
13386         }
13387         
13388         if (this.readOnly) {
13389             input.readonly = true;
13390         }
13391         
13392         if (this.name) {
13393             input.name = this.name;
13394         }
13395         
13396         if (this.size) {
13397             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13398         }
13399         
13400         var settings=this;
13401         ['xs','sm','md','lg'].map(function(size){
13402             if (settings[size]) {
13403                 cfg.cls += ' col-' + size + '-' + settings[size];
13404             }
13405         });
13406         
13407         var inputblock = input;
13408         
13409         if(this.hasFeedback && !this.allowBlank){
13410             
13411             var feedback = {
13412                 tag: 'span',
13413                 cls: 'glyphicon form-control-feedback'
13414             };
13415
13416             inputblock = {
13417                 cls : 'has-feedback',
13418                 cn :  [
13419                     input,
13420                     feedback
13421                 ] 
13422             };  
13423         }
13424         
13425         
13426         if (this.before || this.after) {
13427             
13428             inputblock = {
13429                 cls : 'input-group',
13430                 cn :  [] 
13431             };
13432             if (this.before) {
13433                 inputblock.cn.push({
13434                     tag :'span',
13435                     cls : 'input-group-addon',
13436                     html : this.before
13437                 });
13438             }
13439             
13440             inputblock.cn.push(input);
13441             
13442             if(this.hasFeedback && !this.allowBlank){
13443                 inputblock.cls += ' has-feedback';
13444                 inputblock.cn.push(feedback);
13445             }
13446             
13447             if (this.after) {
13448                 inputblock.cn.push({
13449                     tag :'span',
13450                     cls : 'input-group-addon',
13451                     html : this.after
13452                 });
13453             }
13454             
13455         }
13456         
13457         if (align ==='left' && this.fieldLabel.length) {
13458             cfg.cn = [
13459                 {
13460                     tag: 'label',
13461                     'for' :  id,
13462                     cls : 'control-label',
13463                     html : this.fieldLabel
13464                 },
13465                 {
13466                     cls : "",
13467                     cn: [
13468                         inputblock
13469                     ]
13470                 }
13471
13472             ];
13473             
13474             if(this.labelWidth > 12){
13475                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13476             }
13477
13478             if(this.labelWidth < 13 && this.labelmd == 0){
13479                 this.labelmd = this.labelWidth;
13480             }
13481
13482             if(this.labellg > 0){
13483                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13484                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13485             }
13486
13487             if(this.labelmd > 0){
13488                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13489                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13490             }
13491
13492             if(this.labelsm > 0){
13493                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13494                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13495             }
13496
13497             if(this.labelxs > 0){
13498                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13499                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13500             }
13501             
13502         } else if ( this.fieldLabel.length) {
13503             cfg.cn = [
13504
13505                {
13506                    tag: 'label',
13507                    //cls : 'input-group-addon',
13508                    html : this.fieldLabel
13509
13510                },
13511
13512                inputblock
13513
13514            ];
13515
13516         } else {
13517
13518             cfg.cn = [
13519
13520                 inputblock
13521
13522             ];
13523                 
13524         }
13525         
13526         if (this.disabled) {
13527             input.disabled=true;
13528         }
13529         
13530         return cfg;
13531         
13532     },
13533     /**
13534      * return the real textarea element.
13535      */
13536     inputEl: function ()
13537     {
13538         return this.el.select('textarea.form-control',true).first();
13539     },
13540     
13541     /**
13542      * Clear any invalid styles/messages for this field
13543      */
13544     clearInvalid : function()
13545     {
13546         
13547         if(!this.el || this.preventMark){ // not rendered
13548             return;
13549         }
13550         
13551         var label = this.el.select('label', true).first();
13552         var icon = this.el.select('i.fa-star', true).first();
13553         
13554         if(label && icon){
13555             icon.remove();
13556         }
13557         this.el.removeClass( this.validClass);
13558         this.inputEl().removeClass('is-invalid');
13559          
13560         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13561             
13562             var feedback = this.el.select('.form-control-feedback', true).first();
13563             
13564             if(feedback){
13565                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13566             }
13567             
13568         }
13569         
13570         this.fireEvent('valid', this);
13571     },
13572     
13573      /**
13574      * Mark this field as valid
13575      */
13576     markValid : function()
13577     {
13578         if(!this.el  || this.preventMark){ // not rendered
13579             return;
13580         }
13581         
13582         this.el.removeClass([this.invalidClass, this.validClass]);
13583         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13584         
13585         var feedback = this.el.select('.form-control-feedback', true).first();
13586             
13587         if(feedback){
13588             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13589         }
13590
13591         if(this.disabled || this.allowBlank){
13592             return;
13593         }
13594         
13595         var label = this.el.select('label', true).first();
13596         var icon = this.el.select('i.fa-star', true).first();
13597         
13598         if(label && icon){
13599             icon.remove();
13600         }
13601         if (Roo.bootstrap.version == 3) {
13602             this.el.addClass(this.validClass);
13603         } else {
13604             this.inputEl().addClass('is-valid');
13605         }
13606         
13607         
13608         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13609             
13610             var feedback = this.el.select('.form-control-feedback', true).first();
13611             
13612             if(feedback){
13613                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13614                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13615             }
13616             
13617         }
13618         
13619         this.fireEvent('valid', this);
13620     },
13621     
13622      /**
13623      * Mark this field as invalid
13624      * @param {String} msg The validation message
13625      */
13626     markInvalid : function(msg)
13627     {
13628         if(!this.el  || this.preventMark){ // not rendered
13629             return;
13630         }
13631         
13632         this.el.removeClass([this.invalidClass, this.validClass]);
13633         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13634         
13635         var feedback = this.el.select('.form-control-feedback', true).first();
13636             
13637         if(feedback){
13638             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13639         }
13640
13641         if(this.disabled || this.allowBlank){
13642             return;
13643         }
13644         
13645         var label = this.el.select('label', true).first();
13646         var icon = this.el.select('i.fa-star', true).first();
13647         
13648         if(!this.getValue().length && label && !icon){
13649             this.el.createChild({
13650                 tag : 'i',
13651                 cls : 'text-danger fa fa-lg fa-star',
13652                 tooltip : 'This field is required',
13653                 style : 'margin-right:5px;'
13654             }, label, true);
13655         }
13656         
13657         if (Roo.bootstrap.version == 3) {
13658             this.el.addClass(this.invalidClass);
13659         } else {
13660             this.inputEl().addClass('is-invalid');
13661         }
13662         
13663         // fixme ... this may be depricated need to test..
13664         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13665             
13666             var feedback = this.el.select('.form-control-feedback', true).first();
13667             
13668             if(feedback){
13669                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13670                 
13671                 if(this.getValue().length || this.forceFeedback){
13672                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13673                 }
13674                 
13675             }
13676             
13677         }
13678         
13679         this.fireEvent('invalid', this, msg);
13680     }
13681 });
13682
13683  
13684 /*
13685  * - LGPL
13686  *
13687  * trigger field - base class for combo..
13688  * 
13689  */
13690  
13691 /**
13692  * @class Roo.bootstrap.form.TriggerField
13693  * @extends Roo.bootstrap.form.Input
13694  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13695  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13696  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13697  * for which you can provide a custom implementation.  For example:
13698  * <pre><code>
13699 var trigger = new Roo.bootstrap.form.TriggerField();
13700 trigger.onTriggerClick = myTriggerFn;
13701 trigger.applyTo('my-field');
13702 </code></pre>
13703  *
13704  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13705  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13706  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13707  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13708  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13709
13710  * @constructor
13711  * Create a new TriggerField.
13712  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13713  * to the base TextField)
13714  */
13715 Roo.bootstrap.form.TriggerField = function(config){
13716     this.mimicing = false;
13717     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13718 };
13719
13720 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13721     /**
13722      * @cfg {String} triggerClass A CSS class to apply to the trigger
13723      */
13724      /**
13725      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13726      */
13727     hideTrigger:false,
13728
13729     /**
13730      * @cfg {Boolean} removable (true|false) special filter default false
13731      */
13732     removable : false,
13733     
13734     /** @cfg {Boolean} grow @hide */
13735     /** @cfg {Number} growMin @hide */
13736     /** @cfg {Number} growMax @hide */
13737
13738     /**
13739      * @hide 
13740      * @method
13741      */
13742     autoSize: Roo.emptyFn,
13743     // private
13744     monitorTab : true,
13745     // private
13746     deferHeight : true,
13747
13748     
13749     actionMode : 'wrap',
13750     
13751     caret : false,
13752     
13753     
13754     getAutoCreate : function(){
13755        
13756         var align = this.labelAlign || this.parentLabelAlign();
13757         
13758         var id = Roo.id();
13759         
13760         var cfg = {
13761             cls: 'form-group' //input-group
13762         };
13763         
13764         
13765         var input =  {
13766             tag: 'input',
13767             id : id,
13768             type : this.inputType,
13769             cls : 'form-control',
13770             autocomplete: 'new-password',
13771             placeholder : this.placeholder || '' 
13772             
13773         };
13774         if (this.name) {
13775             input.name = this.name;
13776         }
13777         if (this.size) {
13778             input.cls += ' input-' + this.size;
13779         }
13780         
13781         if (this.disabled) {
13782             input.disabled=true;
13783         }
13784         
13785         var inputblock = input;
13786         
13787         if(this.hasFeedback && !this.allowBlank){
13788             
13789             var feedback = {
13790                 tag: 'span',
13791                 cls: 'glyphicon form-control-feedback'
13792             };
13793             
13794             if(this.removable && !this.editable  ){
13795                 inputblock = {
13796                     cls : 'has-feedback',
13797                     cn :  [
13798                         inputblock,
13799                         {
13800                             tag: 'button',
13801                             html : 'x',
13802                             cls : 'roo-combo-removable-btn close'
13803                         },
13804                         feedback
13805                     ] 
13806                 };
13807             } else {
13808                 inputblock = {
13809                     cls : 'has-feedback',
13810                     cn :  [
13811                         inputblock,
13812                         feedback
13813                     ] 
13814                 };
13815             }
13816
13817         } else {
13818             if(this.removable && !this.editable ){
13819                 inputblock = {
13820                     cls : 'roo-removable',
13821                     cn :  [
13822                         inputblock,
13823                         {
13824                             tag: 'button',
13825                             html : 'x',
13826                             cls : 'roo-combo-removable-btn close'
13827                         }
13828                     ] 
13829                 };
13830             }
13831         }
13832         
13833         if (this.before || this.after) {
13834             
13835             inputblock = {
13836                 cls : 'input-group',
13837                 cn :  [] 
13838             };
13839             if (this.before) {
13840                 inputblock.cn.push({
13841                     tag :'span',
13842                     cls : 'input-group-addon input-group-prepend input-group-text',
13843                     html : this.before
13844                 });
13845             }
13846             
13847             inputblock.cn.push(input);
13848             
13849             if(this.hasFeedback && !this.allowBlank){
13850                 inputblock.cls += ' has-feedback';
13851                 inputblock.cn.push(feedback);
13852             }
13853             
13854             if (this.after) {
13855                 inputblock.cn.push({
13856                     tag :'span',
13857                     cls : 'input-group-addon input-group-append input-group-text',
13858                     html : this.after
13859                 });
13860             }
13861             
13862         };
13863         
13864       
13865         
13866         var ibwrap = inputblock;
13867         
13868         if(this.multiple){
13869             ibwrap = {
13870                 tag: 'ul',
13871                 cls: 'roo-select2-choices',
13872                 cn:[
13873                     {
13874                         tag: 'li',
13875                         cls: 'roo-select2-search-field',
13876                         cn: [
13877
13878                             inputblock
13879                         ]
13880                     }
13881                 ]
13882             };
13883                 
13884         }
13885         
13886         var combobox = {
13887             cls: 'roo-select2-container input-group',
13888             cn: [
13889                  {
13890                     tag: 'input',
13891                     type : 'hidden',
13892                     cls: 'form-hidden-field'
13893                 },
13894                 ibwrap
13895             ]
13896         };
13897         
13898         if(!this.multiple && this.showToggleBtn){
13899             
13900             var caret = {
13901                         tag: 'span',
13902                         cls: 'caret'
13903              };
13904             if (this.caret != false) {
13905                 caret = {
13906                      tag: 'i',
13907                      cls: 'fa fa-' + this.caret
13908                 };
13909                 
13910             }
13911             
13912             combobox.cn.push({
13913                 tag :'span',
13914                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13915                 cn : [
13916                     Roo.bootstrap.version == 3 ? caret : '',
13917                     {
13918                         tag: 'span',
13919                         cls: 'combobox-clear',
13920                         cn  : [
13921                             {
13922                                 tag : 'i',
13923                                 cls: 'icon-remove'
13924                             }
13925                         ]
13926                     }
13927                 ]
13928
13929             })
13930         }
13931         
13932         if(this.multiple){
13933             combobox.cls += ' roo-select2-container-multi';
13934         }
13935          var indicator = {
13936             tag : 'i',
13937             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13938             tooltip : 'This field is required'
13939         };
13940         if (Roo.bootstrap.version == 4) {
13941             indicator = {
13942                 tag : 'i',
13943                 style : 'display:none'
13944             };
13945         }
13946         
13947         
13948         if (align ==='left' && this.fieldLabel.length) {
13949             
13950             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13951
13952             cfg.cn = [
13953                 indicator,
13954                 {
13955                     tag: 'label',
13956                     'for' :  id,
13957                     cls : 'control-label',
13958                     html : this.fieldLabel
13959
13960                 },
13961                 {
13962                     cls : "", 
13963                     cn: [
13964                         combobox
13965                     ]
13966                 }
13967
13968             ];
13969             
13970             var labelCfg = cfg.cn[1];
13971             var contentCfg = cfg.cn[2];
13972             
13973             if(this.indicatorpos == 'right'){
13974                 cfg.cn = [
13975                     {
13976                         tag: 'label',
13977                         'for' :  id,
13978                         cls : 'control-label',
13979                         cn : [
13980                             {
13981                                 tag : 'span',
13982                                 html : this.fieldLabel
13983                             },
13984                             indicator
13985                         ]
13986                     },
13987                     {
13988                         cls : "", 
13989                         cn: [
13990                             combobox
13991                         ]
13992                     }
13993
13994                 ];
13995                 
13996                 labelCfg = cfg.cn[0];
13997                 contentCfg = cfg.cn[1];
13998             }
13999             
14000             if(this.labelWidth > 12){
14001                 labelCfg.style = "width: " + this.labelWidth + 'px';
14002             }
14003             
14004             if(this.labelWidth < 13 && this.labelmd == 0){
14005                 this.labelmd = this.labelWidth;
14006             }
14007             
14008             if(this.labellg > 0){
14009                 labelCfg.cls += ' col-lg-' + this.labellg;
14010                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14011             }
14012             
14013             if(this.labelmd > 0){
14014                 labelCfg.cls += ' col-md-' + this.labelmd;
14015                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14016             }
14017             
14018             if(this.labelsm > 0){
14019                 labelCfg.cls += ' col-sm-' + this.labelsm;
14020                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14021             }
14022             
14023             if(this.labelxs > 0){
14024                 labelCfg.cls += ' col-xs-' + this.labelxs;
14025                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14026             }
14027             
14028         } else if ( this.fieldLabel.length) {
14029 //                Roo.log(" label");
14030             cfg.cn = [
14031                 indicator,
14032                {
14033                    tag: 'label',
14034                    //cls : 'input-group-addon',
14035                    html : this.fieldLabel
14036
14037                },
14038
14039                combobox
14040
14041             ];
14042             
14043             if(this.indicatorpos == 'right'){
14044                 
14045                 cfg.cn = [
14046                     {
14047                        tag: 'label',
14048                        cn : [
14049                            {
14050                                tag : 'span',
14051                                html : this.fieldLabel
14052                            },
14053                            indicator
14054                        ]
14055
14056                     },
14057                     combobox
14058
14059                 ];
14060
14061             }
14062
14063         } else {
14064             
14065 //                Roo.log(" no label && no align");
14066                 cfg = combobox
14067                      
14068                 
14069         }
14070         
14071         var settings=this;
14072         ['xs','sm','md','lg'].map(function(size){
14073             if (settings[size]) {
14074                 cfg.cls += ' col-' + size + '-' + settings[size];
14075             }
14076         });
14077         
14078         return cfg;
14079         
14080     },
14081     
14082     
14083     
14084     // private
14085     onResize : function(w, h){
14086 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14087 //        if(typeof w == 'number'){
14088 //            var x = w - this.trigger.getWidth();
14089 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14090 //            this.trigger.setStyle('left', x+'px');
14091 //        }
14092     },
14093
14094     // private
14095     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14096
14097     // private
14098     getResizeEl : function(){
14099         return this.inputEl();
14100     },
14101
14102     // private
14103     getPositionEl : function(){
14104         return this.inputEl();
14105     },
14106
14107     // private
14108     alignErrorIcon : function(){
14109         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14110     },
14111
14112     // private
14113     initEvents : function(){
14114         
14115         this.createList();
14116         
14117         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14118         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14119         if(!this.multiple && this.showToggleBtn){
14120             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14121             if(this.hideTrigger){
14122                 this.trigger.setDisplayed(false);
14123             }
14124             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14125         }
14126         
14127         if(this.multiple){
14128             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14129         }
14130         
14131         if(this.removable && !this.editable && !this.tickable){
14132             var close = this.closeTriggerEl();
14133             
14134             if(close){
14135                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14136                 close.on('click', this.removeBtnClick, this, close);
14137             }
14138         }
14139         
14140         //this.trigger.addClassOnOver('x-form-trigger-over');
14141         //this.trigger.addClassOnClick('x-form-trigger-click');
14142         
14143         //if(!this.width){
14144         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14145         //}
14146     },
14147     
14148     closeTriggerEl : function()
14149     {
14150         var close = this.el.select('.roo-combo-removable-btn', true).first();
14151         return close ? close : false;
14152     },
14153     
14154     removeBtnClick : function(e, h, el)
14155     {
14156         e.preventDefault();
14157         
14158         if(this.fireEvent("remove", this) !== false){
14159             this.reset();
14160             this.fireEvent("afterremove", this)
14161         }
14162     },
14163     
14164     createList : function()
14165     {
14166         this.list = Roo.get(document.body).createChild({
14167             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14168             cls: 'typeahead typeahead-long dropdown-menu shadow',
14169             style: 'display:none'
14170         });
14171         
14172         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14173         
14174     },
14175
14176     // private
14177     initTrigger : function(){
14178        
14179     },
14180
14181     // private
14182     onDestroy : function(){
14183         if(this.trigger){
14184             this.trigger.removeAllListeners();
14185           //  this.trigger.remove();
14186         }
14187         //if(this.wrap){
14188         //    this.wrap.remove();
14189         //}
14190         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14191     },
14192
14193     // private
14194     onFocus : function(){
14195         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14196         /*
14197         if(!this.mimicing){
14198             this.wrap.addClass('x-trigger-wrap-focus');
14199             this.mimicing = true;
14200             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14201             if(this.monitorTab){
14202                 this.el.on("keydown", this.checkTab, this);
14203             }
14204         }
14205         */
14206     },
14207
14208     // private
14209     checkTab : function(e){
14210         if(e.getKey() == e.TAB){
14211             this.triggerBlur();
14212         }
14213     },
14214
14215     // private
14216     onBlur : function(){
14217         // do nothing
14218     },
14219
14220     // private
14221     mimicBlur : function(e, t){
14222         /*
14223         if(!this.wrap.contains(t) && this.validateBlur()){
14224             this.triggerBlur();
14225         }
14226         */
14227     },
14228
14229     // private
14230     triggerBlur : function(){
14231         this.mimicing = false;
14232         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14233         if(this.monitorTab){
14234             this.el.un("keydown", this.checkTab, this);
14235         }
14236         //this.wrap.removeClass('x-trigger-wrap-focus');
14237         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14238     },
14239
14240     // private
14241     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14242     validateBlur : function(e, t){
14243         return true;
14244     },
14245
14246     // private
14247     onDisable : function(){
14248         this.inputEl().dom.disabled = true;
14249         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14250         //if(this.wrap){
14251         //    this.wrap.addClass('x-item-disabled');
14252         //}
14253     },
14254
14255     // private
14256     onEnable : function(){
14257         this.inputEl().dom.disabled = false;
14258         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14259         //if(this.wrap){
14260         //    this.el.removeClass('x-item-disabled');
14261         //}
14262     },
14263
14264     // private
14265     onShow : function(){
14266         var ae = this.getActionEl();
14267         
14268         if(ae){
14269             ae.dom.style.display = '';
14270             ae.dom.style.visibility = 'visible';
14271         }
14272     },
14273
14274     // private
14275     
14276     onHide : function(){
14277         var ae = this.getActionEl();
14278         ae.dom.style.display = 'none';
14279     },
14280
14281     /**
14282      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14283      * by an implementing function.
14284      * @method
14285      * @param {EventObject} e
14286      */
14287     onTriggerClick : Roo.emptyFn
14288 });
14289  
14290 /*
14291 * Licence: LGPL
14292 */
14293
14294 /**
14295  * @class Roo.bootstrap.form.CardUploader
14296  * @extends Roo.bootstrap.Button
14297  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14298  * @cfg {Number} errorTimeout default 3000
14299  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14300  * @cfg {Array}  html The button text.
14301
14302  *
14303  * @constructor
14304  * Create a new CardUploader
14305  * @param {Object} config The config object
14306  */
14307
14308 Roo.bootstrap.form.CardUploader = function(config){
14309     
14310  
14311     
14312     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14313     
14314     
14315     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14316         return r.data.id
14317      });
14318     
14319      this.addEvents({
14320          // raw events
14321         /**
14322          * @event preview
14323          * When a image is clicked on - and needs to display a slideshow or similar..
14324          * @param {Roo.bootstrap.Card} this
14325          * @param {Object} The image information data 
14326          *
14327          */
14328         'preview' : true,
14329          /**
14330          * @event download
14331          * When a the download link is clicked
14332          * @param {Roo.bootstrap.Card} this
14333          * @param {Object} The image information data  contains 
14334          */
14335         'download' : true
14336         
14337     });
14338 };
14339  
14340 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14341     
14342      
14343     errorTimeout : 3000,
14344      
14345     images : false,
14346    
14347     fileCollection : false,
14348     allowBlank : true,
14349     
14350     getAutoCreate : function()
14351     {
14352         
14353         var cfg =  {
14354             cls :'form-group' ,
14355             cn : [
14356                
14357                 {
14358                     tag: 'label',
14359                    //cls : 'input-group-addon',
14360                     html : this.fieldLabel
14361
14362                 },
14363
14364                 {
14365                     tag: 'input',
14366                     type : 'hidden',
14367                     name : this.name,
14368                     value : this.value,
14369                     cls : 'd-none  form-control'
14370                 },
14371                 
14372                 {
14373                     tag: 'input',
14374                     multiple : 'multiple',
14375                     type : 'file',
14376                     cls : 'd-none  roo-card-upload-selector'
14377                 },
14378                 
14379                 {
14380                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14381                 },
14382                 {
14383                     cls : 'card-columns roo-card-uploader-container'
14384                 }
14385
14386             ]
14387         };
14388            
14389          
14390         return cfg;
14391     },
14392     
14393     getChildContainer : function() /// what children are added to.
14394     {
14395         return this.containerEl;
14396     },
14397    
14398     getButtonContainer : function() /// what children are added to.
14399     {
14400         return this.el.select(".roo-card-uploader-button-container").first();
14401     },
14402    
14403     initEvents : function()
14404     {
14405         
14406         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14407         
14408         var t = this;
14409         this.addxtype({
14410             xns: Roo.bootstrap,
14411
14412             xtype : 'Button',
14413             container_method : 'getButtonContainer' ,            
14414             html :  this.html, // fix changable?
14415             cls : 'w-100 ',
14416             listeners : {
14417                 'click' : function(btn, e) {
14418                     t.onClick(e);
14419                 }
14420             }
14421         });
14422         
14423         
14424         
14425         
14426         this.urlAPI = (window.createObjectURL && window) || 
14427                                 (window.URL && URL.revokeObjectURL && URL) || 
14428                                 (window.webkitURL && webkitURL);
14429                         
14430          
14431          
14432          
14433         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14434         
14435         this.selectorEl.on('change', this.onFileSelected, this);
14436         if (this.images) {
14437             var t = this;
14438             this.images.forEach(function(img) {
14439                 t.addCard(img)
14440             });
14441             this.images = false;
14442         }
14443         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14444          
14445        
14446     },
14447     
14448    
14449     onClick : function(e)
14450     {
14451         e.preventDefault();
14452          
14453         this.selectorEl.dom.click();
14454          
14455     },
14456     
14457     onFileSelected : function(e)
14458     {
14459         e.preventDefault();
14460         
14461         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14462             return;
14463         }
14464         
14465         Roo.each(this.selectorEl.dom.files, function(file){    
14466             this.addFile(file);
14467         }, this);
14468          
14469     },
14470     
14471       
14472     
14473       
14474     
14475     addFile : function(file)
14476     {
14477            
14478         if(typeof(file) === 'string'){
14479             throw "Add file by name?"; // should not happen
14480             return;
14481         }
14482         
14483         if(!file || !this.urlAPI){
14484             return;
14485         }
14486         
14487         // file;
14488         // file.type;
14489         
14490         var _this = this;
14491         
14492         
14493         var url = _this.urlAPI.createObjectURL( file);
14494            
14495         this.addCard({
14496             id : Roo.bootstrap.form.CardUploader.ID--,
14497             is_uploaded : false,
14498             src : url,
14499             srcfile : file,
14500             title : file.name,
14501             mimetype : file.type,
14502             preview : false,
14503             is_deleted : 0
14504         });
14505         
14506     },
14507     
14508     /**
14509      * addCard - add an Attachment to the uploader
14510      * @param data - the data about the image to upload
14511      *
14512      * {
14513           id : 123
14514           title : "Title of file",
14515           is_uploaded : false,
14516           src : "http://.....",
14517           srcfile : { the File upload object },
14518           mimetype : file.type,
14519           preview : false,
14520           is_deleted : 0
14521           .. any other data...
14522         }
14523      *
14524      * 
14525     */
14526     
14527     addCard : function (data)
14528     {
14529         // hidden input element?
14530         // if the file is not an image...
14531         //then we need to use something other that and header_image
14532         var t = this;
14533         //   remove.....
14534         var footer = [
14535             {
14536                 xns : Roo.bootstrap,
14537                 xtype : 'CardFooter',
14538                  items: [
14539                     {
14540                         xns : Roo.bootstrap,
14541                         xtype : 'Element',
14542                         cls : 'd-flex',
14543                         items : [
14544                             
14545                             {
14546                                 xns : Roo.bootstrap,
14547                                 xtype : 'Button',
14548                                 html : String.format("<small>{0}</small>", data.title),
14549                                 cls : 'col-10 text-left',
14550                                 size: 'sm',
14551                                 weight: 'link',
14552                                 fa : 'download',
14553                                 listeners : {
14554                                     click : function() {
14555                                      
14556                                         t.fireEvent( "download", t, data );
14557                                     }
14558                                 }
14559                             },
14560                           
14561                             {
14562                                 xns : Roo.bootstrap,
14563                                 xtype : 'Button',
14564                                 style: 'max-height: 28px; ',
14565                                 size : 'sm',
14566                                 weight: 'danger',
14567                                 cls : 'col-2',
14568                                 fa : 'times',
14569                                 listeners : {
14570                                     click : function() {
14571                                         t.removeCard(data.id)
14572                                     }
14573                                 }
14574                             }
14575                         ]
14576                     }
14577                     
14578                 ] 
14579             }
14580             
14581         ];
14582         
14583         var cn = this.addxtype(
14584             {
14585                  
14586                 xns : Roo.bootstrap,
14587                 xtype : 'Card',
14588                 closeable : true,
14589                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14590                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14591                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14592                 data : data,
14593                 html : false,
14594                  
14595                 items : footer,
14596                 initEvents : function() {
14597                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14598                     var card = this;
14599                     this.imgEl = this.el.select('.card-img-top').first();
14600                     if (this.imgEl) {
14601                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14602                         this.imgEl.set({ 'pointer' : 'cursor' });
14603                                   
14604                     }
14605                     this.getCardFooter().addClass('p-1');
14606                     
14607                   
14608                 }
14609                 
14610             }
14611         );
14612         // dont' really need ot update items.
14613         // this.items.push(cn);
14614         this.fileCollection.add(cn);
14615         
14616         if (!data.srcfile) {
14617             this.updateInput();
14618             return;
14619         }
14620             
14621         var _t = this;
14622         var reader = new FileReader();
14623         reader.addEventListener("load", function() {  
14624             data.srcdata =  reader.result;
14625             _t.updateInput();
14626         });
14627         reader.readAsDataURL(data.srcfile);
14628         
14629         
14630         
14631     },
14632     removeCard : function(id)
14633     {
14634         
14635         var card  = this.fileCollection.get(id);
14636         card.data.is_deleted = 1;
14637         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14638         //this.fileCollection.remove(card);
14639         //this.items = this.items.filter(function(e) { return e != card });
14640         // dont' really need ot update items.
14641         card.el.dom.parentNode.removeChild(card.el.dom);
14642         this.updateInput();
14643
14644         
14645     },
14646     reset: function()
14647     {
14648         this.fileCollection.each(function(card) {
14649             if (card.el.dom && card.el.dom.parentNode) {
14650                 card.el.dom.parentNode.removeChild(card.el.dom);
14651             }
14652         });
14653         this.fileCollection.clear();
14654         this.updateInput();
14655     },
14656     
14657     updateInput : function()
14658     {
14659          var data = [];
14660         this.fileCollection.each(function(e) {
14661             data.push(e.data);
14662             
14663         });
14664         this.inputEl().dom.value = JSON.stringify(data);
14665         
14666         
14667         
14668     }
14669     
14670     
14671 });
14672
14673
14674 Roo.bootstrap.form.CardUploader.ID = -1;/*
14675  * Based on:
14676  * Ext JS Library 1.1.1
14677  * Copyright(c) 2006-2007, Ext JS, LLC.
14678  *
14679  * Originally Released Under LGPL - original licence link has changed is not relivant.
14680  *
14681  * Fork - LGPL
14682  * <script type="text/javascript">
14683  */
14684
14685
14686 /**
14687  * @class Roo.data.SortTypes
14688  * @static
14689  * Defines the default sorting (casting?) comparison functions used when sorting data.
14690  */
14691 Roo.data.SortTypes = {
14692     /**
14693      * Default sort that does nothing
14694      * @param {Mixed} s The value being converted
14695      * @return {Mixed} The comparison value
14696      */
14697     none : function(s){
14698         return s;
14699     },
14700     
14701     /**
14702      * The regular expression used to strip tags
14703      * @type {RegExp}
14704      * @property
14705      */
14706     stripTagsRE : /<\/?[^>]+>/gi,
14707     
14708     /**
14709      * Strips all HTML tags to sort on text only
14710      * @param {Mixed} s The value being converted
14711      * @return {String} The comparison value
14712      */
14713     asText : function(s){
14714         return String(s).replace(this.stripTagsRE, "");
14715     },
14716     
14717     /**
14718      * Strips all HTML tags to sort on text only - Case insensitive
14719      * @param {Mixed} s The value being converted
14720      * @return {String} The comparison value
14721      */
14722     asUCText : function(s){
14723         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14724     },
14725     
14726     /**
14727      * Case insensitive string
14728      * @param {Mixed} s The value being converted
14729      * @return {String} The comparison value
14730      */
14731     asUCString : function(s) {
14732         return String(s).toUpperCase();
14733     },
14734     
14735     /**
14736      * Date sorting
14737      * @param {Mixed} s The value being converted
14738      * @return {Number} The comparison value
14739      */
14740     asDate : function(s) {
14741         if(!s){
14742             return 0;
14743         }
14744         if(s instanceof Date){
14745             return s.getTime();
14746         }
14747         return Date.parse(String(s));
14748     },
14749     
14750     /**
14751      * Float sorting
14752      * @param {Mixed} s The value being converted
14753      * @return {Float} The comparison value
14754      */
14755     asFloat : function(s) {
14756         var val = parseFloat(String(s).replace(/,/g, ""));
14757         if(isNaN(val)) {
14758             val = 0;
14759         }
14760         return val;
14761     },
14762     
14763     /**
14764      * Integer sorting
14765      * @param {Mixed} s The value being converted
14766      * @return {Number} The comparison value
14767      */
14768     asInt : function(s) {
14769         var val = parseInt(String(s).replace(/,/g, ""));
14770         if(isNaN(val)) {
14771             val = 0;
14772         }
14773         return val;
14774     }
14775 };/*
14776  * Based on:
14777  * Ext JS Library 1.1.1
14778  * Copyright(c) 2006-2007, Ext JS, LLC.
14779  *
14780  * Originally Released Under LGPL - original licence link has changed is not relivant.
14781  *
14782  * Fork - LGPL
14783  * <script type="text/javascript">
14784  */
14785
14786 /**
14787 * @class Roo.data.Record
14788  * Instances of this class encapsulate both record <em>definition</em> information, and record
14789  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14790  * to access Records cached in an {@link Roo.data.Store} object.<br>
14791  * <p>
14792  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14793  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14794  * objects.<br>
14795  * <p>
14796  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14797  * @constructor
14798  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14799  * {@link #create}. The parameters are the same.
14800  * @param {Array} data An associative Array of data values keyed by the field name.
14801  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14802  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14803  * not specified an integer id is generated.
14804  */
14805 Roo.data.Record = function(data, id){
14806     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14807     this.data = data;
14808 };
14809
14810 /**
14811  * Generate a constructor for a specific record layout.
14812  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14813  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14814  * Each field definition object may contain the following properties: <ul>
14815  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14816  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14817  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14818  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14819  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14820  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14821  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14822  * this may be omitted.</p></li>
14823  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14824  * <ul><li>auto (Default, implies no conversion)</li>
14825  * <li>string</li>
14826  * <li>int</li>
14827  * <li>float</li>
14828  * <li>boolean</li>
14829  * <li>date</li></ul></p></li>
14830  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14831  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14832  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14833  * by the Reader into an object that will be stored in the Record. It is passed the
14834  * following parameters:<ul>
14835  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14836  * </ul></p></li>
14837  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14838  * </ul>
14839  * <br>usage:<br><pre><code>
14840 var TopicRecord = Roo.data.Record.create(
14841     {name: 'title', mapping: 'topic_title'},
14842     {name: 'author', mapping: 'username'},
14843     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14844     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14845     {name: 'lastPoster', mapping: 'user2'},
14846     {name: 'excerpt', mapping: 'post_text'}
14847 );
14848
14849 var myNewRecord = new TopicRecord({
14850     title: 'Do my job please',
14851     author: 'noobie',
14852     totalPosts: 1,
14853     lastPost: new Date(),
14854     lastPoster: 'Animal',
14855     excerpt: 'No way dude!'
14856 });
14857 myStore.add(myNewRecord);
14858 </code></pre>
14859  * @method create
14860  * @static
14861  */
14862 Roo.data.Record.create = function(o){
14863     var f = function(){
14864         f.superclass.constructor.apply(this, arguments);
14865     };
14866     Roo.extend(f, Roo.data.Record);
14867     var p = f.prototype;
14868     p.fields = new Roo.util.MixedCollection(false, function(field){
14869         return field.name;
14870     });
14871     for(var i = 0, len = o.length; i < len; i++){
14872         p.fields.add(new Roo.data.Field(o[i]));
14873     }
14874     f.getField = function(name){
14875         return p.fields.get(name);  
14876     };
14877     return f;
14878 };
14879
14880 Roo.data.Record.AUTO_ID = 1000;
14881 Roo.data.Record.EDIT = 'edit';
14882 Roo.data.Record.REJECT = 'reject';
14883 Roo.data.Record.COMMIT = 'commit';
14884
14885 Roo.data.Record.prototype = {
14886     /**
14887      * Readonly flag - true if this record has been modified.
14888      * @type Boolean
14889      */
14890     dirty : false,
14891     editing : false,
14892     error: null,
14893     modified: null,
14894
14895     // private
14896     join : function(store){
14897         this.store = store;
14898     },
14899
14900     /**
14901      * Set the named field to the specified value.
14902      * @param {String} name The name of the field to set.
14903      * @param {Object} value The value to set the field to.
14904      */
14905     set : function(name, value){
14906         if(this.data[name] == value){
14907             return;
14908         }
14909         this.dirty = true;
14910         if(!this.modified){
14911             this.modified = {};
14912         }
14913         if(typeof this.modified[name] == 'undefined'){
14914             this.modified[name] = this.data[name];
14915         }
14916         this.data[name] = value;
14917         if(!this.editing && this.store){
14918             this.store.afterEdit(this);
14919         }       
14920     },
14921
14922     /**
14923      * Get the value of the named field.
14924      * @param {String} name The name of the field to get the value of.
14925      * @return {Object} The value of the field.
14926      */
14927     get : function(name){
14928         return this.data[name]; 
14929     },
14930
14931     // private
14932     beginEdit : function(){
14933         this.editing = true;
14934         this.modified = {}; 
14935     },
14936
14937     // private
14938     cancelEdit : function(){
14939         this.editing = false;
14940         delete this.modified;
14941     },
14942
14943     // private
14944     endEdit : function(){
14945         this.editing = false;
14946         if(this.dirty && this.store){
14947             this.store.afterEdit(this);
14948         }
14949     },
14950
14951     /**
14952      * Usually called by the {@link Roo.data.Store} which owns the Record.
14953      * Rejects all changes made to the Record since either creation, or the last commit operation.
14954      * Modified fields are reverted to their original values.
14955      * <p>
14956      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14957      * of reject operations.
14958      */
14959     reject : function(){
14960         var m = this.modified;
14961         for(var n in m){
14962             if(typeof m[n] != "function"){
14963                 this.data[n] = m[n];
14964             }
14965         }
14966         this.dirty = false;
14967         delete this.modified;
14968         this.editing = false;
14969         if(this.store){
14970             this.store.afterReject(this);
14971         }
14972     },
14973
14974     /**
14975      * Usually called by the {@link Roo.data.Store} which owns the Record.
14976      * Commits all changes made to the Record since either creation, or the last commit operation.
14977      * <p>
14978      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14979      * of commit operations.
14980      */
14981     commit : function(){
14982         this.dirty = false;
14983         delete this.modified;
14984         this.editing = false;
14985         if(this.store){
14986             this.store.afterCommit(this);
14987         }
14988     },
14989
14990     // private
14991     hasError : function(){
14992         return this.error != null;
14993     },
14994
14995     // private
14996     clearError : function(){
14997         this.error = null;
14998     },
14999
15000     /**
15001      * Creates a copy of this record.
15002      * @param {String} id (optional) A new record id if you don't want to use this record's id
15003      * @return {Record}
15004      */
15005     copy : function(newId) {
15006         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15007     }
15008 };/*
15009  * Based on:
15010  * Ext JS Library 1.1.1
15011  * Copyright(c) 2006-2007, Ext JS, LLC.
15012  *
15013  * Originally Released Under LGPL - original licence link has changed is not relivant.
15014  *
15015  * Fork - LGPL
15016  * <script type="text/javascript">
15017  */
15018
15019
15020
15021 /**
15022  * @class Roo.data.Store
15023  * @extends Roo.util.Observable
15024  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15025  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15026  * <p>
15027  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15028  * has no knowledge of the format of the data returned by the Proxy.<br>
15029  * <p>
15030  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15031  * instances from the data object. These records are cached and made available through accessor functions.
15032  * @constructor
15033  * Creates a new Store.
15034  * @param {Object} config A config object containing the objects needed for the Store to access data,
15035  * and read the data into Records.
15036  */
15037 Roo.data.Store = function(config){
15038     this.data = new Roo.util.MixedCollection(false);
15039     this.data.getKey = function(o){
15040         return o.id;
15041     };
15042     this.baseParams = {};
15043     // private
15044     this.paramNames = {
15045         "start" : "start",
15046         "limit" : "limit",
15047         "sort" : "sort",
15048         "dir" : "dir",
15049         "multisort" : "_multisort"
15050     };
15051
15052     if(config && config.data){
15053         this.inlineData = config.data;
15054         delete config.data;
15055     }
15056
15057     Roo.apply(this, config);
15058     
15059     if(this.reader){ // reader passed
15060         this.reader = Roo.factory(this.reader, Roo.data);
15061         this.reader.xmodule = this.xmodule || false;
15062         if(!this.recordType){
15063             this.recordType = this.reader.recordType;
15064         }
15065         if(this.reader.onMetaChange){
15066             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15067         }
15068     }
15069
15070     if(this.recordType){
15071         this.fields = this.recordType.prototype.fields;
15072     }
15073     this.modified = [];
15074
15075     this.addEvents({
15076         /**
15077          * @event datachanged
15078          * Fires when the data cache has changed, and a widget which is using this Store
15079          * as a Record cache should refresh its view.
15080          * @param {Store} this
15081          */
15082         datachanged : true,
15083         /**
15084          * @event metachange
15085          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15086          * @param {Store} this
15087          * @param {Object} meta The JSON metadata
15088          */
15089         metachange : true,
15090         /**
15091          * @event add
15092          * Fires when Records have been added to the Store
15093          * @param {Store} this
15094          * @param {Roo.data.Record[]} records The array of Records added
15095          * @param {Number} index The index at which the record(s) were added
15096          */
15097         add : true,
15098         /**
15099          * @event remove
15100          * Fires when a Record has been removed from the Store
15101          * @param {Store} this
15102          * @param {Roo.data.Record} record The Record that was removed
15103          * @param {Number} index The index at which the record was removed
15104          */
15105         remove : true,
15106         /**
15107          * @event update
15108          * Fires when a Record has been updated
15109          * @param {Store} this
15110          * @param {Roo.data.Record} record The Record that was updated
15111          * @param {String} operation The update operation being performed.  Value may be one of:
15112          * <pre><code>
15113  Roo.data.Record.EDIT
15114  Roo.data.Record.REJECT
15115  Roo.data.Record.COMMIT
15116          * </code></pre>
15117          */
15118         update : true,
15119         /**
15120          * @event clear
15121          * Fires when the data cache has been cleared.
15122          * @param {Store} this
15123          */
15124         clear : true,
15125         /**
15126          * @event beforeload
15127          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15128          * the load action will be canceled.
15129          * @param {Store} this
15130          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15131          */
15132         beforeload : true,
15133         /**
15134          * @event beforeloadadd
15135          * Fires after a new set of Records has been loaded.
15136          * @param {Store} this
15137          * @param {Roo.data.Record[]} records The Records that were loaded
15138          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15139          */
15140         beforeloadadd : true,
15141         /**
15142          * @event load
15143          * Fires after a new set of Records has been loaded, before they are added to the store.
15144          * @param {Store} this
15145          * @param {Roo.data.Record[]} records The Records that were loaded
15146          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15147          * @params {Object} return from reader
15148          */
15149         load : true,
15150         /**
15151          * @event loadexception
15152          * Fires if an exception occurs in the Proxy during loading.
15153          * Called with the signature of the Proxy's "loadexception" event.
15154          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15155          * 
15156          * @param {Proxy} 
15157          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15158          * @param {Object} load options 
15159          * @param {Object} jsonData from your request (normally this contains the Exception)
15160          */
15161         loadexception : true
15162     });
15163     
15164     if(this.proxy){
15165         this.proxy = Roo.factory(this.proxy, Roo.data);
15166         this.proxy.xmodule = this.xmodule || false;
15167         this.relayEvents(this.proxy,  ["loadexception"]);
15168     }
15169     this.sortToggle = {};
15170     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15171
15172     Roo.data.Store.superclass.constructor.call(this);
15173
15174     if(this.inlineData){
15175         this.loadData(this.inlineData);
15176         delete this.inlineData;
15177     }
15178 };
15179
15180 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15181      /**
15182     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15183     * without a remote query - used by combo/forms at present.
15184     */
15185     
15186     /**
15187     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15188     */
15189     /**
15190     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15191     */
15192     /**
15193     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15194     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15195     */
15196     /**
15197     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15198     * on any HTTP request
15199     */
15200     /**
15201     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15202     */
15203     /**
15204     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15205     */
15206     multiSort: false,
15207     /**
15208     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15209     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15210     */
15211     remoteSort : false,
15212
15213     /**
15214     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15215      * loaded or when a record is removed. (defaults to false).
15216     */
15217     pruneModifiedRecords : false,
15218
15219     // private
15220     lastOptions : null,
15221
15222     /**
15223      * Add Records to the Store and fires the add event.
15224      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15225      */
15226     add : function(records){
15227         records = [].concat(records);
15228         for(var i = 0, len = records.length; i < len; i++){
15229             records[i].join(this);
15230         }
15231         var index = this.data.length;
15232         this.data.addAll(records);
15233         this.fireEvent("add", this, records, index);
15234     },
15235
15236     /**
15237      * Remove a Record from the Store and fires the remove event.
15238      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15239      */
15240     remove : function(record){
15241         var index = this.data.indexOf(record);
15242         this.data.removeAt(index);
15243  
15244         if(this.pruneModifiedRecords){
15245             this.modified.remove(record);
15246         }
15247         this.fireEvent("remove", this, record, index);
15248     },
15249
15250     /**
15251      * Remove all Records from the Store and fires the clear event.
15252      */
15253     removeAll : function(){
15254         this.data.clear();
15255         if(this.pruneModifiedRecords){
15256             this.modified = [];
15257         }
15258         this.fireEvent("clear", this);
15259     },
15260
15261     /**
15262      * Inserts Records to the Store at the given index and fires the add event.
15263      * @param {Number} index The start index at which to insert the passed Records.
15264      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15265      */
15266     insert : function(index, records){
15267         records = [].concat(records);
15268         for(var i = 0, len = records.length; i < len; i++){
15269             this.data.insert(index, records[i]);
15270             records[i].join(this);
15271         }
15272         this.fireEvent("add", this, records, index);
15273     },
15274
15275     /**
15276      * Get the index within the cache of the passed Record.
15277      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15278      * @return {Number} The index of the passed Record. Returns -1 if not found.
15279      */
15280     indexOf : function(record){
15281         return this.data.indexOf(record);
15282     },
15283
15284     /**
15285      * Get the index within the cache of the Record with the passed id.
15286      * @param {String} id The id of the Record to find.
15287      * @return {Number} The index of the Record. Returns -1 if not found.
15288      */
15289     indexOfId : function(id){
15290         return this.data.indexOfKey(id);
15291     },
15292
15293     /**
15294      * Get the Record with the specified id.
15295      * @param {String} id The id of the Record to find.
15296      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15297      */
15298     getById : function(id){
15299         return this.data.key(id);
15300     },
15301
15302     /**
15303      * Get the Record at the specified index.
15304      * @param {Number} index The index of the Record to find.
15305      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15306      */
15307     getAt : function(index){
15308         return this.data.itemAt(index);
15309     },
15310
15311     /**
15312      * Returns a range of Records between specified indices.
15313      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15314      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15315      * @return {Roo.data.Record[]} An array of Records
15316      */
15317     getRange : function(start, end){
15318         return this.data.getRange(start, end);
15319     },
15320
15321     // private
15322     storeOptions : function(o){
15323         o = Roo.apply({}, o);
15324         delete o.callback;
15325         delete o.scope;
15326         this.lastOptions = o;
15327     },
15328
15329     /**
15330      * Loads the Record cache from the configured Proxy using the configured Reader.
15331      * <p>
15332      * If using remote paging, then the first load call must specify the <em>start</em>
15333      * and <em>limit</em> properties in the options.params property to establish the initial
15334      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15335      * <p>
15336      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15337      * and this call will return before the new data has been loaded. Perform any post-processing
15338      * in a callback function, or in a "load" event handler.</strong>
15339      * <p>
15340      * @param {Object} options An object containing properties which control loading options:<ul>
15341      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15342      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15343      * passed the following arguments:<ul>
15344      * <li>r : Roo.data.Record[]</li>
15345      * <li>options: Options object from the load call</li>
15346      * <li>success: Boolean success indicator</li></ul></li>
15347      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15348      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15349      * </ul>
15350      */
15351     load : function(options){
15352         options = options || {};
15353         if(this.fireEvent("beforeload", this, options) !== false){
15354             this.storeOptions(options);
15355             var p = Roo.apply(options.params || {}, this.baseParams);
15356             // if meta was not loaded from remote source.. try requesting it.
15357             if (!this.reader.metaFromRemote) {
15358                 p._requestMeta = 1;
15359             }
15360             if(this.sortInfo && this.remoteSort){
15361                 var pn = this.paramNames;
15362                 p[pn["sort"]] = this.sortInfo.field;
15363                 p[pn["dir"]] = this.sortInfo.direction;
15364             }
15365             if (this.multiSort) {
15366                 var pn = this.paramNames;
15367                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15368             }
15369             
15370             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15371         }
15372     },
15373
15374     /**
15375      * Reloads the Record cache from the configured Proxy using the configured Reader and
15376      * the options from the last load operation performed.
15377      * @param {Object} options (optional) An object containing properties which may override the options
15378      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15379      * the most recently used options are reused).
15380      */
15381     reload : function(options){
15382         this.load(Roo.applyIf(options||{}, this.lastOptions));
15383     },
15384
15385     // private
15386     // Called as a callback by the Reader during a load operation.
15387     loadRecords : function(o, options, success){
15388          
15389         if(!o){
15390             if(success !== false){
15391                 this.fireEvent("load", this, [], options, o);
15392             }
15393             if(options.callback){
15394                 options.callback.call(options.scope || this, [], options, false);
15395             }
15396             return;
15397         }
15398         // if data returned failure - throw an exception.
15399         if (o.success === false) {
15400             // show a message if no listener is registered.
15401             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15402                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15403             }
15404             // loadmask wil be hooked into this..
15405             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15406             return;
15407         }
15408         var r = o.records, t = o.totalRecords || r.length;
15409         
15410         this.fireEvent("beforeloadadd", this, r, options, o);
15411         
15412         if(!options || options.add !== true){
15413             if(this.pruneModifiedRecords){
15414                 this.modified = [];
15415             }
15416             for(var i = 0, len = r.length; i < len; i++){
15417                 r[i].join(this);
15418             }
15419             if(this.snapshot){
15420                 this.data = this.snapshot;
15421                 delete this.snapshot;
15422             }
15423             this.data.clear();
15424             this.data.addAll(r);
15425             this.totalLength = t;
15426             this.applySort();
15427             this.fireEvent("datachanged", this);
15428         }else{
15429             this.totalLength = Math.max(t, this.data.length+r.length);
15430             this.add(r);
15431         }
15432         
15433         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15434                 
15435             var e = new Roo.data.Record({});
15436
15437             e.set(this.parent.displayField, this.parent.emptyTitle);
15438             e.set(this.parent.valueField, '');
15439
15440             this.insert(0, e);
15441         }
15442             
15443         this.fireEvent("load", this, r, options, o);
15444         if(options.callback){
15445             options.callback.call(options.scope || this, r, options, true);
15446         }
15447     },
15448
15449
15450     /**
15451      * Loads data from a passed data block. A Reader which understands the format of the data
15452      * must have been configured in the constructor.
15453      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15454      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15455      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15456      */
15457     loadData : function(o, append){
15458         var r = this.reader.readRecords(o);
15459         this.loadRecords(r, {add: append}, true);
15460     },
15461     
15462      /**
15463      * using 'cn' the nested child reader read the child array into it's child stores.
15464      * @param {Object} rec The record with a 'children array
15465      */
15466     loadDataFromChildren : function(rec)
15467     {
15468         this.loadData(this.reader.toLoadData(rec));
15469     },
15470     
15471
15472     /**
15473      * Gets the number of cached records.
15474      * <p>
15475      * <em>If using paging, this may not be the total size of the dataset. If the data object
15476      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15477      * the data set size</em>
15478      */
15479     getCount : function(){
15480         return this.data.length || 0;
15481     },
15482
15483     /**
15484      * Gets the total number of records in the dataset as returned by the server.
15485      * <p>
15486      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15487      * the dataset size</em>
15488      */
15489     getTotalCount : function(){
15490         return this.totalLength || 0;
15491     },
15492
15493     /**
15494      * Returns the sort state of the Store as an object with two properties:
15495      * <pre><code>
15496  field {String} The name of the field by which the Records are sorted
15497  direction {String} The sort order, "ASC" or "DESC"
15498      * </code></pre>
15499      */
15500     getSortState : function(){
15501         return this.sortInfo;
15502     },
15503
15504     // private
15505     applySort : function(){
15506         if(this.sortInfo && !this.remoteSort){
15507             var s = this.sortInfo, f = s.field;
15508             var st = this.fields.get(f).sortType;
15509             var fn = function(r1, r2){
15510                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15511                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15512             };
15513             this.data.sort(s.direction, fn);
15514             if(this.snapshot && this.snapshot != this.data){
15515                 this.snapshot.sort(s.direction, fn);
15516             }
15517         }
15518     },
15519
15520     /**
15521      * Sets the default sort column and order to be used by the next load operation.
15522      * @param {String} fieldName The name of the field to sort by.
15523      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15524      */
15525     setDefaultSort : function(field, dir){
15526         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15527     },
15528
15529     /**
15530      * Sort the Records.
15531      * If remote sorting is used, the sort is performed on the server, and the cache is
15532      * reloaded. If local sorting is used, the cache is sorted internally.
15533      * @param {String} fieldName The name of the field to sort by.
15534      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15535      */
15536     sort : function(fieldName, dir){
15537         var f = this.fields.get(fieldName);
15538         if(!dir){
15539             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15540             
15541             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15542                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15543             }else{
15544                 dir = f.sortDir;
15545             }
15546         }
15547         this.sortToggle[f.name] = dir;
15548         this.sortInfo = {field: f.name, direction: dir};
15549         if(!this.remoteSort){
15550             this.applySort();
15551             this.fireEvent("datachanged", this);
15552         }else{
15553             this.load(this.lastOptions);
15554         }
15555     },
15556
15557     /**
15558      * Calls the specified function for each of the Records in the cache.
15559      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15560      * Returning <em>false</em> aborts and exits the iteration.
15561      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15562      */
15563     each : function(fn, scope){
15564         this.data.each(fn, scope);
15565     },
15566
15567     /**
15568      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15569      * (e.g., during paging).
15570      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15571      */
15572     getModifiedRecords : function(){
15573         return this.modified;
15574     },
15575
15576     // private
15577     createFilterFn : function(property, value, anyMatch){
15578         if(!value.exec){ // not a regex
15579             value = String(value);
15580             if(value.length == 0){
15581                 return false;
15582             }
15583             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15584         }
15585         return function(r){
15586             return value.test(r.data[property]);
15587         };
15588     },
15589
15590     /**
15591      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15592      * @param {String} property A field on your records
15593      * @param {Number} start The record index to start at (defaults to 0)
15594      * @param {Number} end The last record index to include (defaults to length - 1)
15595      * @return {Number} The sum
15596      */
15597     sum : function(property, start, end){
15598         var rs = this.data.items, v = 0;
15599         start = start || 0;
15600         end = (end || end === 0) ? end : rs.length-1;
15601
15602         for(var i = start; i <= end; i++){
15603             v += (rs[i].data[property] || 0);
15604         }
15605         return v;
15606     },
15607
15608     /**
15609      * Filter the records by a specified property.
15610      * @param {String} field A field on your records
15611      * @param {String/RegExp} value Either a string that the field
15612      * should start with or a RegExp to test against the field
15613      * @param {Boolean} anyMatch True to match any part not just the beginning
15614      */
15615     filter : function(property, value, anyMatch){
15616         var fn = this.createFilterFn(property, value, anyMatch);
15617         return fn ? this.filterBy(fn) : this.clearFilter();
15618     },
15619
15620     /**
15621      * Filter by a function. The specified function will be called with each
15622      * record in this data source. If the function returns true the record is included,
15623      * otherwise it is filtered.
15624      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15625      * @param {Object} scope (optional) The scope of the function (defaults to this)
15626      */
15627     filterBy : function(fn, scope){
15628         this.snapshot = this.snapshot || this.data;
15629         this.data = this.queryBy(fn, scope||this);
15630         this.fireEvent("datachanged", this);
15631     },
15632
15633     /**
15634      * Query the records by a specified property.
15635      * @param {String} field A field on your records
15636      * @param {String/RegExp} value Either a string that the field
15637      * should start with or a RegExp to test against the field
15638      * @param {Boolean} anyMatch True to match any part not just the beginning
15639      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15640      */
15641     query : function(property, value, anyMatch){
15642         var fn = this.createFilterFn(property, value, anyMatch);
15643         return fn ? this.queryBy(fn) : this.data.clone();
15644     },
15645
15646     /**
15647      * Query by a function. The specified function will be called with each
15648      * record in this data source. If the function returns true the record is included
15649      * in the results.
15650      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15651      * @param {Object} scope (optional) The scope of the function (defaults to this)
15652       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15653      **/
15654     queryBy : function(fn, scope){
15655         var data = this.snapshot || this.data;
15656         return data.filterBy(fn, scope||this);
15657     },
15658
15659     /**
15660      * Collects unique values for a particular dataIndex from this store.
15661      * @param {String} dataIndex The property to collect
15662      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15663      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15664      * @return {Array} An array of the unique values
15665      **/
15666     collect : function(dataIndex, allowNull, bypassFilter){
15667         var d = (bypassFilter === true && this.snapshot) ?
15668                 this.snapshot.items : this.data.items;
15669         var v, sv, r = [], l = {};
15670         for(var i = 0, len = d.length; i < len; i++){
15671             v = d[i].data[dataIndex];
15672             sv = String(v);
15673             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15674                 l[sv] = true;
15675                 r[r.length] = v;
15676             }
15677         }
15678         return r;
15679     },
15680
15681     /**
15682      * Revert to a view of the Record cache with no filtering applied.
15683      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15684      */
15685     clearFilter : function(suppressEvent){
15686         if(this.snapshot && this.snapshot != this.data){
15687             this.data = this.snapshot;
15688             delete this.snapshot;
15689             if(suppressEvent !== true){
15690                 this.fireEvent("datachanged", this);
15691             }
15692         }
15693     },
15694
15695     // private
15696     afterEdit : function(record){
15697         if(this.modified.indexOf(record) == -1){
15698             this.modified.push(record);
15699         }
15700         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15701     },
15702     
15703     // private
15704     afterReject : function(record){
15705         this.modified.remove(record);
15706         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15707     },
15708
15709     // private
15710     afterCommit : function(record){
15711         this.modified.remove(record);
15712         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15713     },
15714
15715     /**
15716      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15717      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15718      */
15719     commitChanges : function(){
15720         var m = this.modified.slice(0);
15721         this.modified = [];
15722         for(var i = 0, len = m.length; i < len; i++){
15723             m[i].commit();
15724         }
15725     },
15726
15727     /**
15728      * Cancel outstanding changes on all changed records.
15729      */
15730     rejectChanges : function(){
15731         var m = this.modified.slice(0);
15732         this.modified = [];
15733         for(var i = 0, len = m.length; i < len; i++){
15734             m[i].reject();
15735         }
15736     },
15737
15738     onMetaChange : function(meta, rtype, o){
15739         this.recordType = rtype;
15740         this.fields = rtype.prototype.fields;
15741         delete this.snapshot;
15742         this.sortInfo = meta.sortInfo || this.sortInfo;
15743         this.modified = [];
15744         this.fireEvent('metachange', this, this.reader.meta);
15745     },
15746     
15747     moveIndex : function(data, type)
15748     {
15749         var index = this.indexOf(data);
15750         
15751         var newIndex = index + type;
15752         
15753         this.remove(data);
15754         
15755         this.insert(newIndex, data);
15756         
15757     }
15758 });/*
15759  * Based on:
15760  * Ext JS Library 1.1.1
15761  * Copyright(c) 2006-2007, Ext JS, LLC.
15762  *
15763  * Originally Released Under LGPL - original licence link has changed is not relivant.
15764  *
15765  * Fork - LGPL
15766  * <script type="text/javascript">
15767  */
15768
15769 /**
15770  * @class Roo.data.SimpleStore
15771  * @extends Roo.data.Store
15772  * Small helper class to make creating Stores from Array data easier.
15773  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15774  * @cfg {Array} fields An array of field definition objects, or field name strings.
15775  * @cfg {Object} an existing reader (eg. copied from another store)
15776  * @cfg {Array} data The multi-dimensional array of data
15777  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15778  * @cfg {Roo.data.Reader} reader  [not-required] 
15779  * @constructor
15780  * @param {Object} config
15781  */
15782 Roo.data.SimpleStore = function(config)
15783 {
15784     Roo.data.SimpleStore.superclass.constructor.call(this, {
15785         isLocal : true,
15786         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15787                 id: config.id
15788             },
15789             Roo.data.Record.create(config.fields)
15790         ),
15791         proxy : new Roo.data.MemoryProxy(config.data)
15792     });
15793     this.load();
15794 };
15795 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15796  * Based on:
15797  * Ext JS Library 1.1.1
15798  * Copyright(c) 2006-2007, Ext JS, LLC.
15799  *
15800  * Originally Released Under LGPL - original licence link has changed is not relivant.
15801  *
15802  * Fork - LGPL
15803  * <script type="text/javascript">
15804  */
15805
15806 /**
15807 /**
15808  * @extends Roo.data.Store
15809  * @class Roo.data.JsonStore
15810  * Small helper class to make creating Stores for JSON data easier. <br/>
15811 <pre><code>
15812 var store = new Roo.data.JsonStore({
15813     url: 'get-images.php',
15814     root: 'images',
15815     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15816 });
15817 </code></pre>
15818  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15819  * JsonReader and HttpProxy (unless inline data is provided).</b>
15820  * @cfg {Array} fields An array of field definition objects, or field name strings.
15821  * @constructor
15822  * @param {Object} config
15823  */
15824 Roo.data.JsonStore = function(c){
15825     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15826         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15827         reader: new Roo.data.JsonReader(c, c.fields)
15828     }));
15829 };
15830 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15831  * Based on:
15832  * Ext JS Library 1.1.1
15833  * Copyright(c) 2006-2007, Ext JS, LLC.
15834  *
15835  * Originally Released Under LGPL - original licence link has changed is not relivant.
15836  *
15837  * Fork - LGPL
15838  * <script type="text/javascript">
15839  */
15840
15841  
15842 Roo.data.Field = function(config){
15843     if(typeof config == "string"){
15844         config = {name: config};
15845     }
15846     Roo.apply(this, config);
15847     
15848     if(!this.type){
15849         this.type = "auto";
15850     }
15851     
15852     var st = Roo.data.SortTypes;
15853     // named sortTypes are supported, here we look them up
15854     if(typeof this.sortType == "string"){
15855         this.sortType = st[this.sortType];
15856     }
15857     
15858     // set default sortType for strings and dates
15859     if(!this.sortType){
15860         switch(this.type){
15861             case "string":
15862                 this.sortType = st.asUCString;
15863                 break;
15864             case "date":
15865                 this.sortType = st.asDate;
15866                 break;
15867             default:
15868                 this.sortType = st.none;
15869         }
15870     }
15871
15872     // define once
15873     var stripRe = /[\$,%]/g;
15874
15875     // prebuilt conversion function for this field, instead of
15876     // switching every time we're reading a value
15877     if(!this.convert){
15878         var cv, dateFormat = this.dateFormat;
15879         switch(this.type){
15880             case "":
15881             case "auto":
15882             case undefined:
15883                 cv = function(v){ return v; };
15884                 break;
15885             case "string":
15886                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15887                 break;
15888             case "int":
15889                 cv = function(v){
15890                     return v !== undefined && v !== null && v !== '' ?
15891                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15892                     };
15893                 break;
15894             case "float":
15895                 cv = function(v){
15896                     return v !== undefined && v !== null && v !== '' ?
15897                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15898                     };
15899                 break;
15900             case "bool":
15901             case "boolean":
15902                 cv = function(v){ return v === true || v === "true" || v == 1; };
15903                 break;
15904             case "date":
15905                 cv = function(v){
15906                     if(!v){
15907                         return '';
15908                     }
15909                     if(v instanceof Date){
15910                         return v;
15911                     }
15912                     if(dateFormat){
15913                         if(dateFormat == "timestamp"){
15914                             return new Date(v*1000);
15915                         }
15916                         return Date.parseDate(v, dateFormat);
15917                     }
15918                     var parsed = Date.parse(v);
15919                     return parsed ? new Date(parsed) : null;
15920                 };
15921              break;
15922             
15923         }
15924         this.convert = cv;
15925     }
15926 };
15927
15928 Roo.data.Field.prototype = {
15929     dateFormat: null,
15930     defaultValue: "",
15931     mapping: null,
15932     sortType : null,
15933     sortDir : "ASC"
15934 };/*
15935  * Based on:
15936  * Ext JS Library 1.1.1
15937  * Copyright(c) 2006-2007, Ext JS, LLC.
15938  *
15939  * Originally Released Under LGPL - original licence link has changed is not relivant.
15940  *
15941  * Fork - LGPL
15942  * <script type="text/javascript">
15943  */
15944  
15945 // Base class for reading structured data from a data source.  This class is intended to be
15946 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15947
15948 /**
15949  * @class Roo.data.DataReader
15950  * @abstract
15951  * Base class for reading structured data from a data source.  This class is intended to be
15952  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15953  */
15954
15955 Roo.data.DataReader = function(meta, recordType){
15956     
15957     this.meta = meta;
15958     
15959     this.recordType = recordType instanceof Array ? 
15960         Roo.data.Record.create(recordType) : recordType;
15961 };
15962
15963 Roo.data.DataReader.prototype = {
15964     
15965     
15966     readerType : 'Data',
15967      /**
15968      * Create an empty record
15969      * @param {Object} data (optional) - overlay some values
15970      * @return {Roo.data.Record} record created.
15971      */
15972     newRow :  function(d) {
15973         var da =  {};
15974         this.recordType.prototype.fields.each(function(c) {
15975             switch( c.type) {
15976                 case 'int' : da[c.name] = 0; break;
15977                 case 'date' : da[c.name] = new Date(); break;
15978                 case 'float' : da[c.name] = 0.0; break;
15979                 case 'boolean' : da[c.name] = false; break;
15980                 default : da[c.name] = ""; break;
15981             }
15982             
15983         });
15984         return new this.recordType(Roo.apply(da, d));
15985     }
15986     
15987     
15988 };/*
15989  * Based on:
15990  * Ext JS Library 1.1.1
15991  * Copyright(c) 2006-2007, Ext JS, LLC.
15992  *
15993  * Originally Released Under LGPL - original licence link has changed is not relivant.
15994  *
15995  * Fork - LGPL
15996  * <script type="text/javascript">
15997  */
15998
15999 /**
16000  * @class Roo.data.DataProxy
16001  * @extends Roo.util.Observable
16002  * @abstract
16003  * This class is an abstract base class for implementations which provide retrieval of
16004  * unformatted data objects.<br>
16005  * <p>
16006  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16007  * (of the appropriate type which knows how to parse the data object) to provide a block of
16008  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16009  * <p>
16010  * Custom implementations must implement the load method as described in
16011  * {@link Roo.data.HttpProxy#load}.
16012  */
16013 Roo.data.DataProxy = function(){
16014     this.addEvents({
16015         /**
16016          * @event beforeload
16017          * Fires before a network request is made to retrieve a data object.
16018          * @param {Object} This DataProxy object.
16019          * @param {Object} params The params parameter to the load function.
16020          */
16021         beforeload : true,
16022         /**
16023          * @event load
16024          * Fires before the load method's callback is called.
16025          * @param {Object} This DataProxy object.
16026          * @param {Object} o The data object.
16027          * @param {Object} arg The callback argument object passed to the load function.
16028          */
16029         load : true,
16030         /**
16031          * @event loadexception
16032          * Fires if an Exception occurs during data retrieval.
16033          * @param {Object} This DataProxy object.
16034          * @param {Object} o The data object.
16035          * @param {Object} arg The callback argument object passed to the load function.
16036          * @param {Object} e The Exception.
16037          */
16038         loadexception : true
16039     });
16040     Roo.data.DataProxy.superclass.constructor.call(this);
16041 };
16042
16043 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16044
16045     /**
16046      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16047      */
16048 /*
16049  * Based on:
16050  * Ext JS Library 1.1.1
16051  * Copyright(c) 2006-2007, Ext JS, LLC.
16052  *
16053  * Originally Released Under LGPL - original licence link has changed is not relivant.
16054  *
16055  * Fork - LGPL
16056  * <script type="text/javascript">
16057  */
16058 /**
16059  * @class Roo.data.MemoryProxy
16060  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16061  * to the Reader when its load method is called.
16062  * @constructor
16063  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16064  */
16065 Roo.data.MemoryProxy = function(data){
16066     if (data.data) {
16067         data = data.data;
16068     }
16069     Roo.data.MemoryProxy.superclass.constructor.call(this);
16070     this.data = data;
16071 };
16072
16073 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16074     
16075     /**
16076      * Load data from the requested source (in this case an in-memory
16077      * data object passed to the constructor), read the data object into
16078      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16079      * process that block using the passed callback.
16080      * @param {Object} params This parameter is not used by the MemoryProxy class.
16081      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16082      * object into a block of Roo.data.Records.
16083      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16084      * The function must be passed <ul>
16085      * <li>The Record block object</li>
16086      * <li>The "arg" argument from the load function</li>
16087      * <li>A boolean success indicator</li>
16088      * </ul>
16089      * @param {Object} scope The scope in which to call the callback
16090      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16091      */
16092     load : function(params, reader, callback, scope, arg){
16093         params = params || {};
16094         var result;
16095         try {
16096             result = reader.readRecords(params.data ? params.data :this.data);
16097         }catch(e){
16098             this.fireEvent("loadexception", this, arg, null, e);
16099             callback.call(scope, null, arg, false);
16100             return;
16101         }
16102         callback.call(scope, result, arg, true);
16103     },
16104     
16105     // private
16106     update : function(params, records){
16107         
16108     }
16109 });/*
16110  * Based on:
16111  * Ext JS Library 1.1.1
16112  * Copyright(c) 2006-2007, Ext JS, LLC.
16113  *
16114  * Originally Released Under LGPL - original licence link has changed is not relivant.
16115  *
16116  * Fork - LGPL
16117  * <script type="text/javascript">
16118  */
16119 /**
16120  * @class Roo.data.HttpProxy
16121  * @extends Roo.data.DataProxy
16122  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16123  * configured to reference a certain URL.<br><br>
16124  * <p>
16125  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16126  * from which the running page was served.<br><br>
16127  * <p>
16128  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16129  * <p>
16130  * Be aware that to enable the browser to parse an XML document, the server must set
16131  * the Content-Type header in the HTTP response to "text/xml".
16132  * @constructor
16133  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16134  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16135  * will be used to make the request.
16136  */
16137 Roo.data.HttpProxy = function(conn){
16138     Roo.data.HttpProxy.superclass.constructor.call(this);
16139     // is conn a conn config or a real conn?
16140     this.conn = conn;
16141     this.useAjax = !conn || !conn.events;
16142   
16143 };
16144
16145 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16146     // thse are take from connection...
16147     
16148     /**
16149      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16150      */
16151     /**
16152      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16153      * extra parameters to each request made by this object. (defaults to undefined)
16154      */
16155     /**
16156      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16157      *  to each request made by this object. (defaults to undefined)
16158      */
16159     /**
16160      * @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)
16161      */
16162     /**
16163      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16164      */
16165      /**
16166      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16167      * @type Boolean
16168      */
16169   
16170
16171     /**
16172      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16173      * @type Boolean
16174      */
16175     /**
16176      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16177      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16178      * a finer-grained basis than the DataProxy events.
16179      */
16180     getConnection : function(){
16181         return this.useAjax ? Roo.Ajax : this.conn;
16182     },
16183
16184     /**
16185      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16186      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16187      * process that block using the passed callback.
16188      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16189      * for the request to the remote server.
16190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16191      * object into a block of Roo.data.Records.
16192      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16193      * The function must be passed <ul>
16194      * <li>The Record block object</li>
16195      * <li>The "arg" argument from the load function</li>
16196      * <li>A boolean success indicator</li>
16197      * </ul>
16198      * @param {Object} scope The scope in which to call the callback
16199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16200      */
16201     load : function(params, reader, callback, scope, arg){
16202         if(this.fireEvent("beforeload", this, params) !== false){
16203             var  o = {
16204                 params : params || {},
16205                 request: {
16206                     callback : callback,
16207                     scope : scope,
16208                     arg : arg
16209                 },
16210                 reader: reader,
16211                 callback : this.loadResponse,
16212                 scope: this
16213             };
16214             if(this.useAjax){
16215                 Roo.applyIf(o, this.conn);
16216                 if(this.activeRequest){
16217                     Roo.Ajax.abort(this.activeRequest);
16218                 }
16219                 this.activeRequest = Roo.Ajax.request(o);
16220             }else{
16221                 this.conn.request(o);
16222             }
16223         }else{
16224             callback.call(scope||this, null, arg, false);
16225         }
16226     },
16227
16228     // private
16229     loadResponse : function(o, success, response){
16230         delete this.activeRequest;
16231         if(!success){
16232             this.fireEvent("loadexception", this, o, response);
16233             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16234             return;
16235         }
16236         var result;
16237         try {
16238             result = o.reader.read(response);
16239         }catch(e){
16240             this.fireEvent("loadexception", this, o, response, e);
16241             o.request.callback.call(o.request.scope, {
16242                     success : false,
16243                     raw : {
16244                         errorMsg : response.responseText
16245                     }
16246                     
16247                 }, o.request.arg, false);
16248             return;
16249         }
16250         
16251         this.fireEvent("load", this, o, o.request.arg);
16252         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16253     },
16254
16255     // private
16256     update : function(dataSet){
16257
16258     },
16259
16260     // private
16261     updateResponse : function(dataSet){
16262
16263     }
16264 });/*
16265  * Based on:
16266  * Ext JS Library 1.1.1
16267  * Copyright(c) 2006-2007, Ext JS, LLC.
16268  *
16269  * Originally Released Under LGPL - original licence link has changed is not relivant.
16270  *
16271  * Fork - LGPL
16272  * <script type="text/javascript">
16273  */
16274
16275 /**
16276  * @class Roo.data.ScriptTagProxy
16277  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16278  * other than the originating domain of the running page.<br><br>
16279  * <p>
16280  * <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
16281  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16282  * <p>
16283  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16284  * source code that is used as the source inside a &lt;script> tag.<br><br>
16285  * <p>
16286  * In order for the browser to process the returned data, the server must wrap the data object
16287  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16288  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16289  * depending on whether the callback name was passed:
16290  * <p>
16291  * <pre><code>
16292 boolean scriptTag = false;
16293 String cb = request.getParameter("callback");
16294 if (cb != null) {
16295     scriptTag = true;
16296     response.setContentType("text/javascript");
16297 } else {
16298     response.setContentType("application/x-json");
16299 }
16300 Writer out = response.getWriter();
16301 if (scriptTag) {
16302     out.write(cb + "(");
16303 }
16304 out.print(dataBlock.toJsonString());
16305 if (scriptTag) {
16306     out.write(");");
16307 }
16308 </pre></code>
16309  *
16310  * @constructor
16311  * @param {Object} config A configuration object.
16312  */
16313 Roo.data.ScriptTagProxy = function(config){
16314     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16315     Roo.apply(this, config);
16316     this.head = document.getElementsByTagName("head")[0];
16317 };
16318
16319 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16320
16321 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16322     /**
16323      * @cfg {String} url The URL from which to request the data object.
16324      */
16325     /**
16326      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16327      */
16328     timeout : 30000,
16329     /**
16330      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16331      * the server the name of the callback function set up by the load call to process the returned data object.
16332      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16333      * javascript output which calls this named function passing the data object as its only parameter.
16334      */
16335     callbackParam : "callback",
16336     /**
16337      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16338      * name to the request.
16339      */
16340     nocache : true,
16341
16342     /**
16343      * Load data from the configured URL, read the data object into
16344      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16345      * process that block using the passed callback.
16346      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16347      * for the request to the remote server.
16348      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16349      * object into a block of Roo.data.Records.
16350      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16351      * The function must be passed <ul>
16352      * <li>The Record block object</li>
16353      * <li>The "arg" argument from the load function</li>
16354      * <li>A boolean success indicator</li>
16355      * </ul>
16356      * @param {Object} scope The scope in which to call the callback
16357      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16358      */
16359     load : function(params, reader, callback, scope, arg){
16360         if(this.fireEvent("beforeload", this, params) !== false){
16361
16362             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16363
16364             var url = this.url;
16365             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16366             if(this.nocache){
16367                 url += "&_dc=" + (new Date().getTime());
16368             }
16369             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16370             var trans = {
16371                 id : transId,
16372                 cb : "stcCallback"+transId,
16373                 scriptId : "stcScript"+transId,
16374                 params : params,
16375                 arg : arg,
16376                 url : url,
16377                 callback : callback,
16378                 scope : scope,
16379                 reader : reader
16380             };
16381             var conn = this;
16382
16383             window[trans.cb] = function(o){
16384                 conn.handleResponse(o, trans);
16385             };
16386
16387             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16388
16389             if(this.autoAbort !== false){
16390                 this.abort();
16391             }
16392
16393             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16394
16395             var script = document.createElement("script");
16396             script.setAttribute("src", url);
16397             script.setAttribute("type", "text/javascript");
16398             script.setAttribute("id", trans.scriptId);
16399             this.head.appendChild(script);
16400
16401             this.trans = trans;
16402         }else{
16403             callback.call(scope||this, null, arg, false);
16404         }
16405     },
16406
16407     // private
16408     isLoading : function(){
16409         return this.trans ? true : false;
16410     },
16411
16412     /**
16413      * Abort the current server request.
16414      */
16415     abort : function(){
16416         if(this.isLoading()){
16417             this.destroyTrans(this.trans);
16418         }
16419     },
16420
16421     // private
16422     destroyTrans : function(trans, isLoaded){
16423         this.head.removeChild(document.getElementById(trans.scriptId));
16424         clearTimeout(trans.timeoutId);
16425         if(isLoaded){
16426             window[trans.cb] = undefined;
16427             try{
16428                 delete window[trans.cb];
16429             }catch(e){}
16430         }else{
16431             // if hasn't been loaded, wait for load to remove it to prevent script error
16432             window[trans.cb] = function(){
16433                 window[trans.cb] = undefined;
16434                 try{
16435                     delete window[trans.cb];
16436                 }catch(e){}
16437             };
16438         }
16439     },
16440
16441     // private
16442     handleResponse : function(o, trans){
16443         this.trans = false;
16444         this.destroyTrans(trans, true);
16445         var result;
16446         try {
16447             result = trans.reader.readRecords(o);
16448         }catch(e){
16449             this.fireEvent("loadexception", this, o, trans.arg, e);
16450             trans.callback.call(trans.scope||window, null, trans.arg, false);
16451             return;
16452         }
16453         this.fireEvent("load", this, o, trans.arg);
16454         trans.callback.call(trans.scope||window, result, trans.arg, true);
16455     },
16456
16457     // private
16458     handleFailure : function(trans){
16459         this.trans = false;
16460         this.destroyTrans(trans, false);
16461         this.fireEvent("loadexception", this, null, trans.arg);
16462         trans.callback.call(trans.scope||window, null, trans.arg, false);
16463     }
16464 });/*
16465  * Based on:
16466  * Ext JS Library 1.1.1
16467  * Copyright(c) 2006-2007, Ext JS, LLC.
16468  *
16469  * Originally Released Under LGPL - original licence link has changed is not relivant.
16470  *
16471  * Fork - LGPL
16472  * <script type="text/javascript">
16473  */
16474
16475 /**
16476  * @class Roo.data.JsonReader
16477  * @extends Roo.data.DataReader
16478  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16479  * based on mappings in a provided Roo.data.Record constructor.
16480  * 
16481  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16482  * in the reply previously. 
16483  * 
16484  * <p>
16485  * Example code:
16486  * <pre><code>
16487 var RecordDef = Roo.data.Record.create([
16488     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16489     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16490 ]);
16491 var myReader = new Roo.data.JsonReader({
16492     totalProperty: "results",    // The property which contains the total dataset size (optional)
16493     root: "rows",                // The property which contains an Array of row objects
16494     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16495 }, RecordDef);
16496 </code></pre>
16497  * <p>
16498  * This would consume a JSON file like this:
16499  * <pre><code>
16500 { 'results': 2, 'rows': [
16501     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16502     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16503 }
16504 </code></pre>
16505  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16506  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16507  * paged from the remote server.
16508  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16509  * @cfg {String} root name of the property which contains the Array of row objects.
16510  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16511  * @cfg {Array} fields Array of field definition objects
16512  * @constructor
16513  * Create a new JsonReader
16514  * @param {Object} meta Metadata configuration options
16515  * @param {Object} recordType Either an Array of field definition objects,
16516  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16517  */
16518 Roo.data.JsonReader = function(meta, recordType){
16519     
16520     meta = meta || {};
16521     // set some defaults:
16522     Roo.applyIf(meta, {
16523         totalProperty: 'total',
16524         successProperty : 'success',
16525         root : 'data',
16526         id : 'id'
16527     });
16528     
16529     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16530 };
16531 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16532     
16533     readerType : 'Json',
16534     
16535     /**
16536      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16537      * Used by Store query builder to append _requestMeta to params.
16538      * 
16539      */
16540     metaFromRemote : false,
16541     /**
16542      * This method is only used by a DataProxy which has retrieved data from a remote server.
16543      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16544      * @return {Object} data A data block which is used by an Roo.data.Store object as
16545      * a cache of Roo.data.Records.
16546      */
16547     read : function(response){
16548         var json = response.responseText;
16549        
16550         var o = /* eval:var:o */ eval("("+json+")");
16551         if(!o) {
16552             throw {message: "JsonReader.read: Json object not found"};
16553         }
16554         
16555         if(o.metaData){
16556             
16557             delete this.ef;
16558             this.metaFromRemote = true;
16559             this.meta = o.metaData;
16560             this.recordType = Roo.data.Record.create(o.metaData.fields);
16561             this.onMetaChange(this.meta, this.recordType, o);
16562         }
16563         return this.readRecords(o);
16564     },
16565
16566     // private function a store will implement
16567     onMetaChange : function(meta, recordType, o){
16568
16569     },
16570
16571     /**
16572          * @ignore
16573          */
16574     simpleAccess: function(obj, subsc) {
16575         return obj[subsc];
16576     },
16577
16578         /**
16579          * @ignore
16580          */
16581     getJsonAccessor: function(){
16582         var re = /[\[\.]/;
16583         return function(expr) {
16584             try {
16585                 return(re.test(expr))
16586                     ? new Function("obj", "return obj." + expr)
16587                     : function(obj){
16588                         return obj[expr];
16589                     };
16590             } catch(e){}
16591             return Roo.emptyFn;
16592         };
16593     }(),
16594
16595     /**
16596      * Create a data block containing Roo.data.Records from an XML document.
16597      * @param {Object} o An object which contains an Array of row objects in the property specified
16598      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16599      * which contains the total size of the dataset.
16600      * @return {Object} data A data block which is used by an Roo.data.Store object as
16601      * a cache of Roo.data.Records.
16602      */
16603     readRecords : function(o){
16604         /**
16605          * After any data loads, the raw JSON data is available for further custom processing.
16606          * @type Object
16607          */
16608         this.o = o;
16609         var s = this.meta, Record = this.recordType,
16610             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16611
16612 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16613         if (!this.ef) {
16614             if(s.totalProperty) {
16615                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16616                 }
16617                 if(s.successProperty) {
16618                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16619                 }
16620                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16621                 if (s.id) {
16622                         var g = this.getJsonAccessor(s.id);
16623                         this.getId = function(rec) {
16624                                 var r = g(rec);  
16625                                 return (r === undefined || r === "") ? null : r;
16626                         };
16627                 } else {
16628                         this.getId = function(){return null;};
16629                 }
16630             this.ef = [];
16631             for(var jj = 0; jj < fl; jj++){
16632                 f = fi[jj];
16633                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16634                 this.ef[jj] = this.getJsonAccessor(map);
16635             }
16636         }
16637
16638         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16639         if(s.totalProperty){
16640             var vt = parseInt(this.getTotal(o), 10);
16641             if(!isNaN(vt)){
16642                 totalRecords = vt;
16643             }
16644         }
16645         if(s.successProperty){
16646             var vs = this.getSuccess(o);
16647             if(vs === false || vs === 'false'){
16648                 success = false;
16649             }
16650         }
16651         var records = [];
16652         for(var i = 0; i < c; i++){
16653                 var n = root[i];
16654             var values = {};
16655             var id = this.getId(n);
16656             for(var j = 0; j < fl; j++){
16657                 f = fi[j];
16658             var v = this.ef[j](n);
16659             if (!f.convert) {
16660                 Roo.log('missing convert for ' + f.name);
16661                 Roo.log(f);
16662                 continue;
16663             }
16664             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16665             }
16666             var record = new Record(values, id);
16667             record.json = n;
16668             records[i] = record;
16669         }
16670         return {
16671             raw : o,
16672             success : success,
16673             records : records,
16674             totalRecords : totalRecords
16675         };
16676     },
16677     // used when loading children.. @see loadDataFromChildren
16678     toLoadData: function(rec)
16679     {
16680         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16681         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16682         return { data : data, total : data.length };
16683         
16684     }
16685 });/*
16686  * Based on:
16687  * Ext JS Library 1.1.1
16688  * Copyright(c) 2006-2007, Ext JS, LLC.
16689  *
16690  * Originally Released Under LGPL - original licence link has changed is not relivant.
16691  *
16692  * Fork - LGPL
16693  * <script type="text/javascript">
16694  */
16695
16696 /**
16697  * @class Roo.data.ArrayReader
16698  * @extends Roo.data.DataReader
16699  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16700  * Each element of that Array represents a row of data fields. The
16701  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16702  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16703  * <p>
16704  * Example code:.
16705  * <pre><code>
16706 var RecordDef = Roo.data.Record.create([
16707     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16708     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16709 ]);
16710 var myReader = new Roo.data.ArrayReader({
16711     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16712 }, RecordDef);
16713 </code></pre>
16714  * <p>
16715  * This would consume an Array like this:
16716  * <pre><code>
16717 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16718   </code></pre>
16719  
16720  * @constructor
16721  * Create a new JsonReader
16722  * @param {Object} meta Metadata configuration options.
16723  * @param {Object|Array} recordType Either an Array of field definition objects
16724  * 
16725  * @cfg {Array} fields Array of field definition objects
16726  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16727  * as specified to {@link Roo.data.Record#create},
16728  * or an {@link Roo.data.Record} object
16729  *
16730  * 
16731  * created using {@link Roo.data.Record#create}.
16732  */
16733 Roo.data.ArrayReader = function(meta, recordType)
16734 {    
16735     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16736 };
16737
16738 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16739     
16740       /**
16741      * Create a data block containing Roo.data.Records from an XML document.
16742      * @param {Object} o An Array of row objects which represents the dataset.
16743      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16744      * a cache of Roo.data.Records.
16745      */
16746     readRecords : function(o)
16747     {
16748         var sid = this.meta ? this.meta.id : null;
16749         var recordType = this.recordType, fields = recordType.prototype.fields;
16750         var records = [];
16751         var root = o;
16752         for(var i = 0; i < root.length; i++){
16753             var n = root[i];
16754             var values = {};
16755             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16756             for(var j = 0, jlen = fields.length; j < jlen; j++){
16757                 var f = fields.items[j];
16758                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16759                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16760                 v = f.convert(v);
16761                 values[f.name] = v;
16762             }
16763             var record = new recordType(values, id);
16764             record.json = n;
16765             records[records.length] = record;
16766         }
16767         return {
16768             records : records,
16769             totalRecords : records.length
16770         };
16771     },
16772     // used when loading children.. @see loadDataFromChildren
16773     toLoadData: function(rec)
16774     {
16775         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16776         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16777         
16778     }
16779     
16780     
16781 });/*
16782  * - LGPL
16783  * * 
16784  */
16785
16786 /**
16787  * @class Roo.bootstrap.form.ComboBox
16788  * @extends Roo.bootstrap.form.TriggerField
16789  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16790  * @cfg {Boolean} append (true|false) default false
16791  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16792  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16793  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16794  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16795  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16796  * @cfg {Boolean} animate default true
16797  * @cfg {Boolean} emptyResultText only for touch device
16798  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16799  * @cfg {String} emptyTitle default ''
16800  * @cfg {Number} width fixed with? experimental
16801  * @constructor
16802  * Create a new ComboBox.
16803  * @param {Object} config Configuration options
16804  */
16805 Roo.bootstrap.form.ComboBox = function(config){
16806     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16807     this.addEvents({
16808         /**
16809          * @event expand
16810          * Fires when the dropdown list is expanded
16811         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16812         */
16813         'expand' : true,
16814         /**
16815          * @event collapse
16816          * Fires when the dropdown list is collapsed
16817         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16818         */
16819         'collapse' : true,
16820         /**
16821          * @event beforeselect
16822          * Fires before a list item is selected. Return false to cancel the selection.
16823         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16824         * @param {Roo.data.Record} record The data record returned from the underlying store
16825         * @param {Number} index The index of the selected item in the dropdown list
16826         */
16827         'beforeselect' : true,
16828         /**
16829          * @event select
16830          * Fires when a list item is selected
16831         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16832         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16833         * @param {Number} index The index of the selected item in the dropdown list
16834         */
16835         'select' : true,
16836         /**
16837          * @event beforequery
16838          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16839          * The event object passed has these properties:
16840         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16841         * @param {String} query The query
16842         * @param {Boolean} forceAll true to force "all" query
16843         * @param {Boolean} cancel true to cancel the query
16844         * @param {Object} e The query event object
16845         */
16846         'beforequery': true,
16847          /**
16848          * @event add
16849          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16850         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16851         */
16852         'add' : true,
16853         /**
16854          * @event edit
16855          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16858         */
16859         'edit' : true,
16860         /**
16861          * @event remove
16862          * Fires when the remove value from the combobox array
16863         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16864         */
16865         'remove' : true,
16866         /**
16867          * @event afterremove
16868          * Fires when the remove value from the combobox array
16869         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16870         */
16871         'afterremove' : true,
16872         /**
16873          * @event specialfilter
16874          * Fires when specialfilter
16875             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16876             */
16877         'specialfilter' : true,
16878         /**
16879          * @event tick
16880          * Fires when tick the element
16881             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16882             */
16883         'tick' : true,
16884         /**
16885          * @event touchviewdisplay
16886          * Fires when touch view require special display (default is using displayField)
16887             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16888             * @param {Object} cfg set html .
16889             */
16890         'touchviewdisplay' : true
16891         
16892     });
16893     
16894     this.item = [];
16895     this.tickItems = [];
16896     
16897     this.selectedIndex = -1;
16898     if(this.mode == 'local'){
16899         if(config.queryDelay === undefined){
16900             this.queryDelay = 10;
16901         }
16902         if(config.minChars === undefined){
16903             this.minChars = 0;
16904         }
16905     }
16906 };
16907
16908 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16909      
16910     /**
16911      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16912      * rendering into an Roo.Editor, defaults to false)
16913      */
16914     /**
16915      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16916      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16917      */
16918     /**
16919      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16920      */
16921     /**
16922      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16923      * the dropdown list (defaults to undefined, with no header element)
16924      */
16925
16926      /**
16927      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16928      */
16929      
16930      /**
16931      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16932      */
16933     listWidth: undefined,
16934     /**
16935      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16936      * mode = 'remote' or 'text' if mode = 'local')
16937      */
16938     displayField: undefined,
16939     
16940     /**
16941      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16942      * mode = 'remote' or 'value' if mode = 'local'). 
16943      * Note: use of a valueField requires the user make a selection
16944      * in order for a value to be mapped.
16945      */
16946     valueField: undefined,
16947     /**
16948      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16949      */
16950     modalTitle : '',
16951     
16952     /**
16953      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16954      * field's data value (defaults to the underlying DOM element's name)
16955      */
16956     hiddenName: undefined,
16957     /**
16958      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16959      */
16960     listClass: '',
16961     /**
16962      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16963      */
16964     selectedClass: 'active',
16965     
16966     /**
16967      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16968      */
16969     shadow:'sides',
16970     /**
16971      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16972      * anchor positions (defaults to 'tl-bl')
16973      */
16974     listAlign: 'tl-bl?',
16975     /**
16976      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16977      */
16978     maxHeight: 300,
16979     /**
16980      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16981      * query specified by the allQuery config option (defaults to 'query')
16982      */
16983     triggerAction: 'query',
16984     /**
16985      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16986      * (defaults to 4, does not apply if editable = false)
16987      */
16988     minChars : 4,
16989     /**
16990      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16991      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16992      */
16993     typeAhead: false,
16994     /**
16995      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16996      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16997      */
16998     queryDelay: 500,
16999     /**
17000      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17001      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17002      */
17003     pageSize: 0,
17004     /**
17005      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17006      * when editable = true (defaults to false)
17007      */
17008     selectOnFocus:false,
17009     /**
17010      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17011      */
17012     queryParam: 'query',
17013     /**
17014      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17015      * when mode = 'remote' (defaults to 'Loading...')
17016      */
17017     loadingText: 'Loading...',
17018     /**
17019      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17020      */
17021     resizable: false,
17022     /**
17023      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17024      */
17025     handleHeight : 8,
17026     /**
17027      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17028      * traditional select (defaults to true)
17029      */
17030     editable: true,
17031     /**
17032      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17033      */
17034     allQuery: '',
17035     /**
17036      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17037      */
17038     mode: 'remote',
17039     /**
17040      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17041      * listWidth has a higher value)
17042      */
17043     minListWidth : 70,
17044     /**
17045      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17046      * allow the user to set arbitrary text into the field (defaults to false)
17047      */
17048     forceSelection:false,
17049     /**
17050      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17051      * if typeAhead = true (defaults to 250)
17052      */
17053     typeAheadDelay : 250,
17054     /**
17055      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17056      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17057      */
17058     valueNotFoundText : undefined,
17059     /**
17060      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17061      */
17062     blockFocus : false,
17063     
17064     /**
17065      * @cfg {Boolean} disableClear Disable showing of clear button.
17066      */
17067     disableClear : false,
17068     /**
17069      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17070      */
17071     alwaysQuery : false,
17072     
17073     /**
17074      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17075      */
17076     multiple : false,
17077     
17078     /**
17079      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17080      */
17081     invalidClass : "has-warning",
17082     
17083     /**
17084      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17085      */
17086     validClass : "has-success",
17087     
17088     /**
17089      * @cfg {Boolean} specialFilter (true|false) special filter default false
17090      */
17091     specialFilter : false,
17092     
17093     /**
17094      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17095      */
17096     mobileTouchView : true,
17097     
17098     /**
17099      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17100      */
17101     useNativeIOS : false,
17102     
17103     /**
17104      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17105      */
17106     mobile_restrict_height : false,
17107     
17108     ios_options : false,
17109     
17110     //private
17111     addicon : false,
17112     editicon: false,
17113     
17114     page: 0,
17115     hasQuery: false,
17116     append: false,
17117     loadNext: false,
17118     autoFocus : true,
17119     tickable : false,
17120     btnPosition : 'right',
17121     triggerList : true,
17122     showToggleBtn : true,
17123     animate : true,
17124     emptyResultText: 'Empty',
17125     triggerText : 'Select',
17126     emptyTitle : '',
17127     width : false,
17128     
17129     // element that contains real text value.. (when hidden is used..)
17130     
17131     getAutoCreate : function()
17132     {   
17133         var cfg = false;
17134         //render
17135         /*
17136          * Render classic select for iso
17137          */
17138         
17139         if(Roo.isIOS && this.useNativeIOS){
17140             cfg = this.getAutoCreateNativeIOS();
17141             return cfg;
17142         }
17143         
17144         /*
17145          * Touch Devices
17146          */
17147         
17148         if(Roo.isTouch && this.mobileTouchView){
17149             cfg = this.getAutoCreateTouchView();
17150             return cfg;;
17151         }
17152         
17153         /*
17154          *  Normal ComboBox
17155          */
17156         if(!this.tickable){
17157             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17158             return cfg;
17159         }
17160         
17161         /*
17162          *  ComboBox with tickable selections
17163          */
17164              
17165         var align = this.labelAlign || this.parentLabelAlign();
17166         
17167         cfg = {
17168             cls : 'form-group roo-combobox-tickable' //input-group
17169         };
17170         
17171         var btn_text_select = '';
17172         var btn_text_done = '';
17173         var btn_text_cancel = '';
17174         
17175         if (this.btn_text_show) {
17176             btn_text_select = 'Select';
17177             btn_text_done = 'Done';
17178             btn_text_cancel = 'Cancel'; 
17179         }
17180         
17181         var buttons = {
17182             tag : 'div',
17183             cls : 'tickable-buttons',
17184             cn : [
17185                 {
17186                     tag : 'button',
17187                     type : 'button',
17188                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17189                     //html : this.triggerText
17190                     html: btn_text_select
17191                 },
17192                 {
17193                     tag : 'button',
17194                     type : 'button',
17195                     name : 'ok',
17196                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17197                     //html : 'Done'
17198                     html: btn_text_done
17199                 },
17200                 {
17201                     tag : 'button',
17202                     type : 'button',
17203                     name : 'cancel',
17204                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17205                     //html : 'Cancel'
17206                     html: btn_text_cancel
17207                 }
17208             ]
17209         };
17210         
17211         if(this.editable){
17212             buttons.cn.unshift({
17213                 tag: 'input',
17214                 cls: 'roo-select2-search-field-input'
17215             });
17216         }
17217         
17218         var _this = this;
17219         
17220         Roo.each(buttons.cn, function(c){
17221             if (_this.size) {
17222                 c.cls += ' btn-' + _this.size;
17223             }
17224
17225             if (_this.disabled) {
17226                 c.disabled = true;
17227             }
17228         });
17229         
17230         var box = {
17231             tag: 'div',
17232             style : 'display: contents',
17233             cn: [
17234                 {
17235                     tag: 'input',
17236                     type : 'hidden',
17237                     cls: 'form-hidden-field'
17238                 },
17239                 {
17240                     tag: 'ul',
17241                     cls: 'roo-select2-choices',
17242                     cn:[
17243                         {
17244                             tag: 'li',
17245                             cls: 'roo-select2-search-field',
17246                             cn: [
17247                                 buttons
17248                             ]
17249                         }
17250                     ]
17251                 }
17252             ]
17253         };
17254         
17255         var combobox = {
17256             cls: 'roo-select2-container input-group roo-select2-container-multi',
17257             cn: [
17258                 
17259                 box
17260 //                {
17261 //                    tag: 'ul',
17262 //                    cls: 'typeahead typeahead-long dropdown-menu',
17263 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17264 //                }
17265             ]
17266         };
17267         
17268         if(this.hasFeedback && !this.allowBlank){
17269             
17270             var feedback = {
17271                 tag: 'span',
17272                 cls: 'glyphicon form-control-feedback'
17273             };
17274
17275             combobox.cn.push(feedback);
17276         }
17277         
17278         
17279         
17280         var indicator = {
17281             tag : 'i',
17282             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17283             tooltip : 'This field is required'
17284         };
17285         if (Roo.bootstrap.version == 4) {
17286             indicator = {
17287                 tag : 'i',
17288                 style : 'display:none'
17289             };
17290         }
17291         if (align ==='left' && this.fieldLabel.length) {
17292             
17293             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17294             
17295             cfg.cn = [
17296                 indicator,
17297                 {
17298                     tag: 'label',
17299                     'for' :  id,
17300                     cls : 'control-label col-form-label',
17301                     html : this.fieldLabel
17302
17303                 },
17304                 {
17305                     cls : "", 
17306                     cn: [
17307                         combobox
17308                     ]
17309                 }
17310
17311             ];
17312             
17313             var labelCfg = cfg.cn[1];
17314             var contentCfg = cfg.cn[2];
17315             
17316
17317             if(this.indicatorpos == 'right'){
17318                 
17319                 cfg.cn = [
17320                     {
17321                         tag: 'label',
17322                         'for' :  id,
17323                         cls : 'control-label col-form-label',
17324                         cn : [
17325                             {
17326                                 tag : 'span',
17327                                 html : this.fieldLabel
17328                             },
17329                             indicator
17330                         ]
17331                     },
17332                     {
17333                         cls : "",
17334                         cn: [
17335                             combobox
17336                         ]
17337                     }
17338
17339                 ];
17340                 
17341                 
17342                 
17343                 labelCfg = cfg.cn[0];
17344                 contentCfg = cfg.cn[1];
17345             
17346             }
17347             
17348             if(this.labelWidth > 12){
17349                 labelCfg.style = "width: " + this.labelWidth + 'px';
17350             }
17351             if(this.width * 1 > 0){
17352                 contentCfg.style = "width: " + this.width + 'px';
17353             }
17354             if(this.labelWidth < 13 && this.labelmd == 0){
17355                 this.labelmd = this.labelWidth;
17356             }
17357             
17358             if(this.labellg > 0){
17359                 labelCfg.cls += ' col-lg-' + this.labellg;
17360                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17361             }
17362             
17363             if(this.labelmd > 0){
17364                 labelCfg.cls += ' col-md-' + this.labelmd;
17365                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17366             }
17367             
17368             if(this.labelsm > 0){
17369                 labelCfg.cls += ' col-sm-' + this.labelsm;
17370                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17371             }
17372             
17373             if(this.labelxs > 0){
17374                 labelCfg.cls += ' col-xs-' + this.labelxs;
17375                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17376             }
17377                 
17378                 
17379         } else if ( this.fieldLabel.length) {
17380 //                Roo.log(" label");
17381                  cfg.cn = [
17382                    indicator,
17383                     {
17384                         tag: 'label',
17385                         //cls : 'input-group-addon',
17386                         html : this.fieldLabel
17387                     },
17388                     combobox
17389                 ];
17390                 
17391                 if(this.indicatorpos == 'right'){
17392                     cfg.cn = [
17393                         {
17394                             tag: 'label',
17395                             //cls : 'input-group-addon',
17396                             html : this.fieldLabel
17397                         },
17398                         indicator,
17399                         combobox
17400                     ];
17401                     
17402                 }
17403
17404         } else {
17405             
17406 //                Roo.log(" no label && no align");
17407                 cfg = combobox
17408                      
17409                 
17410         }
17411          
17412         var settings=this;
17413         ['xs','sm','md','lg'].map(function(size){
17414             if (settings[size]) {
17415                 cfg.cls += ' col-' + size + '-' + settings[size];
17416             }
17417         });
17418         
17419         return cfg;
17420         
17421     },
17422     
17423     _initEventsCalled : false,
17424     
17425     // private
17426     initEvents: function()
17427     {   
17428         if (this._initEventsCalled) { // as we call render... prevent looping...
17429             return;
17430         }
17431         this._initEventsCalled = true;
17432         
17433         if (!this.store) {
17434             throw "can not find store for combo";
17435         }
17436         
17437         this.indicator = this.indicatorEl();
17438         
17439         this.store = Roo.factory(this.store, Roo.data);
17440         this.store.parent = this;
17441         
17442         // if we are building from html. then this element is so complex, that we can not really
17443         // use the rendered HTML.
17444         // so we have to trash and replace the previous code.
17445         if (Roo.XComponent.build_from_html) {
17446             // remove this element....
17447             var e = this.el.dom, k=0;
17448             while (e ) { e = e.previousSibling;  ++k;}
17449
17450             this.el.remove();
17451             
17452             this.el=false;
17453             this.rendered = false;
17454             
17455             this.render(this.parent().getChildContainer(true), k);
17456         }
17457         
17458         if(Roo.isIOS && this.useNativeIOS){
17459             this.initIOSView();
17460             return;
17461         }
17462         
17463         /*
17464          * Touch Devices
17465          */
17466         
17467         if(Roo.isTouch && this.mobileTouchView){
17468             this.initTouchView();
17469             return;
17470         }
17471         
17472         if(this.tickable){
17473             this.initTickableEvents();
17474             return;
17475         }
17476         
17477         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17478         
17479         if(this.hiddenName){
17480             
17481             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17482             
17483             this.hiddenField.dom.value =
17484                 this.hiddenValue !== undefined ? this.hiddenValue :
17485                 this.value !== undefined ? this.value : '';
17486
17487             // prevent input submission
17488             this.el.dom.removeAttribute('name');
17489             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17490              
17491              
17492         }
17493         //if(Roo.isGecko){
17494         //    this.el.dom.setAttribute('autocomplete', 'off');
17495         //}
17496         
17497         var cls = 'x-combo-list';
17498         
17499         //this.list = new Roo.Layer({
17500         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17501         //});
17502         
17503         var _this = this;
17504         
17505         (function(){
17506             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17507             _this.list.setWidth(lw);
17508         }).defer(100);
17509         
17510         this.list.on('mouseover', this.onViewOver, this);
17511         this.list.on('mousemove', this.onViewMove, this);
17512         this.list.on('scroll', this.onViewScroll, this);
17513         
17514         /*
17515         this.list.swallowEvent('mousewheel');
17516         this.assetHeight = 0;
17517
17518         if(this.title){
17519             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17520             this.assetHeight += this.header.getHeight();
17521         }
17522
17523         this.innerList = this.list.createChild({cls:cls+'-inner'});
17524         this.innerList.on('mouseover', this.onViewOver, this);
17525         this.innerList.on('mousemove', this.onViewMove, this);
17526         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17527         
17528         if(this.allowBlank && !this.pageSize && !this.disableClear){
17529             this.footer = this.list.createChild({cls:cls+'-ft'});
17530             this.pageTb = new Roo.Toolbar(this.footer);
17531            
17532         }
17533         if(this.pageSize){
17534             this.footer = this.list.createChild({cls:cls+'-ft'});
17535             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17536                     {pageSize: this.pageSize});
17537             
17538         }
17539         
17540         if (this.pageTb && this.allowBlank && !this.disableClear) {
17541             var _this = this;
17542             this.pageTb.add(new Roo.Toolbar.Fill(), {
17543                 cls: 'x-btn-icon x-btn-clear',
17544                 text: '&#160;',
17545                 handler: function()
17546                 {
17547                     _this.collapse();
17548                     _this.clearValue();
17549                     _this.onSelect(false, -1);
17550                 }
17551             });
17552         }
17553         if (this.footer) {
17554             this.assetHeight += this.footer.getHeight();
17555         }
17556         */
17557             
17558         if(!this.tpl){
17559             this.tpl = Roo.bootstrap.version == 4 ?
17560                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17561                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17562         }
17563
17564         this.view = new Roo.View(this.list, this.tpl, {
17565             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17566         });
17567         //this.view.wrapEl.setDisplayed(false);
17568         this.view.on('click', this.onViewClick, this);
17569         
17570         
17571         this.store.on('beforeload', this.onBeforeLoad, this);
17572         this.store.on('load', this.onLoad, this);
17573         this.store.on('loadexception', this.onLoadException, this);
17574         /*
17575         if(this.resizable){
17576             this.resizer = new Roo.Resizable(this.list,  {
17577                pinned:true, handles:'se'
17578             });
17579             this.resizer.on('resize', function(r, w, h){
17580                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17581                 this.listWidth = w;
17582                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17583                 this.restrictHeight();
17584             }, this);
17585             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17586         }
17587         */
17588         if(!this.editable){
17589             this.editable = true;
17590             this.setEditable(false);
17591         }
17592         
17593         /*
17594         
17595         if (typeof(this.events.add.listeners) != 'undefined') {
17596             
17597             this.addicon = this.wrap.createChild(
17598                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17599        
17600             this.addicon.on('click', function(e) {
17601                 this.fireEvent('add', this);
17602             }, this);
17603         }
17604         if (typeof(this.events.edit.listeners) != 'undefined') {
17605             
17606             this.editicon = this.wrap.createChild(
17607                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17608             if (this.addicon) {
17609                 this.editicon.setStyle('margin-left', '40px');
17610             }
17611             this.editicon.on('click', function(e) {
17612                 
17613                 // we fire even  if inothing is selected..
17614                 this.fireEvent('edit', this, this.lastData );
17615                 
17616             }, this);
17617         }
17618         */
17619         
17620         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17621             "up" : function(e){
17622                 this.inKeyMode = true;
17623                 this.selectPrev();
17624             },
17625
17626             "down" : function(e){
17627                 if(!this.isExpanded()){
17628                     this.onTriggerClick();
17629                 }else{
17630                     this.inKeyMode = true;
17631                     this.selectNext();
17632                 }
17633             },
17634
17635             "enter" : function(e){
17636 //                this.onViewClick();
17637                 //return true;
17638                 this.collapse();
17639                 
17640                 if(this.fireEvent("specialkey", this, e)){
17641                     this.onViewClick(false);
17642                 }
17643                 
17644                 return true;
17645             },
17646
17647             "esc" : function(e){
17648                 this.collapse();
17649             },
17650
17651             "tab" : function(e){
17652                 this.collapse();
17653                 
17654                 if(this.fireEvent("specialkey", this, e)){
17655                     this.onViewClick(false);
17656                 }
17657                 
17658                 return true;
17659             },
17660
17661             scope : this,
17662
17663             doRelay : function(foo, bar, hname){
17664                 if(hname == 'down' || this.scope.isExpanded()){
17665                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17666                 }
17667                 return true;
17668             },
17669
17670             forceKeyDown: true
17671         });
17672         
17673         
17674         this.queryDelay = Math.max(this.queryDelay || 10,
17675                 this.mode == 'local' ? 10 : 250);
17676         
17677         
17678         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17679         
17680         if(this.typeAhead){
17681             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17682         }
17683         if(this.editable !== false){
17684             this.inputEl().on("keyup", this.onKeyUp, this);
17685         }
17686         if(this.forceSelection){
17687             this.inputEl().on('blur', this.doForce, this);
17688         }
17689         
17690         if(this.multiple){
17691             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17692             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17693         }
17694     },
17695     
17696     initTickableEvents: function()
17697     {   
17698         this.createList();
17699         
17700         if(this.hiddenName){
17701             
17702             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17703             
17704             this.hiddenField.dom.value =
17705                 this.hiddenValue !== undefined ? this.hiddenValue :
17706                 this.value !== undefined ? this.value : '';
17707
17708             // prevent input submission
17709             this.el.dom.removeAttribute('name');
17710             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17711              
17712              
17713         }
17714         
17715 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17716         
17717         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17718         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17719         if(this.triggerList){
17720             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17721         }
17722          
17723         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17724         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17725         
17726         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17727         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17728         
17729         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17730         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17731         
17732         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17733         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17734         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17735         
17736         this.okBtn.hide();
17737         this.cancelBtn.hide();
17738         
17739         var _this = this;
17740         
17741         (function(){
17742             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17743             _this.list.setWidth(lw);
17744         }).defer(100);
17745         
17746         this.list.on('mouseover', this.onViewOver, this);
17747         this.list.on('mousemove', this.onViewMove, this);
17748         
17749         this.list.on('scroll', this.onViewScroll, this);
17750         
17751         if(!this.tpl){
17752             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17753                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17754         }
17755
17756         this.view = new Roo.View(this.list, this.tpl, {
17757             singleSelect:true,
17758             tickable:true,
17759             parent:this,
17760             store: this.store,
17761             selectedClass: this.selectedClass
17762         });
17763         
17764         //this.view.wrapEl.setDisplayed(false);
17765         this.view.on('click', this.onViewClick, this);
17766         
17767         
17768         
17769         this.store.on('beforeload', this.onBeforeLoad, this);
17770         this.store.on('load', this.onLoad, this);
17771         this.store.on('loadexception', this.onLoadException, this);
17772         
17773         if(this.editable){
17774             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17775                 "up" : function(e){
17776                     this.inKeyMode = true;
17777                     this.selectPrev();
17778                 },
17779
17780                 "down" : function(e){
17781                     this.inKeyMode = true;
17782                     this.selectNext();
17783                 },
17784
17785                 "enter" : function(e){
17786                     if(this.fireEvent("specialkey", this, e)){
17787                         this.onViewClick(false);
17788                     }
17789                     
17790                     return true;
17791                 },
17792
17793                 "esc" : function(e){
17794                     this.onTickableFooterButtonClick(e, false, false);
17795                 },
17796
17797                 "tab" : function(e){
17798                     this.fireEvent("specialkey", this, e);
17799                     
17800                     this.onTickableFooterButtonClick(e, false, false);
17801                     
17802                     return true;
17803                 },
17804
17805                 scope : this,
17806
17807                 doRelay : function(e, fn, key){
17808                     if(this.scope.isExpanded()){
17809                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17810                     }
17811                     return true;
17812                 },
17813
17814                 forceKeyDown: true
17815             });
17816         }
17817         
17818         this.queryDelay = Math.max(this.queryDelay || 10,
17819                 this.mode == 'local' ? 10 : 250);
17820         
17821         
17822         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17823         
17824         if(this.typeAhead){
17825             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17826         }
17827         
17828         if(this.editable !== false){
17829             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17830         }
17831         
17832         this.indicator = this.indicatorEl();
17833         
17834         if(this.indicator){
17835             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17836             this.indicator.hide();
17837         }
17838         
17839     },
17840
17841     onDestroy : function(){
17842         if(this.view){
17843             this.view.setStore(null);
17844             this.view.el.removeAllListeners();
17845             this.view.el.remove();
17846             this.view.purgeListeners();
17847         }
17848         if(this.list){
17849             this.list.dom.innerHTML  = '';
17850         }
17851         
17852         if(this.store){
17853             this.store.un('beforeload', this.onBeforeLoad, this);
17854             this.store.un('load', this.onLoad, this);
17855             this.store.un('loadexception', this.onLoadException, this);
17856         }
17857         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17858     },
17859
17860     // private
17861     fireKey : function(e){
17862         if(e.isNavKeyPress() && !this.list.isVisible()){
17863             this.fireEvent("specialkey", this, e);
17864         }
17865     },
17866
17867     // private
17868     onResize: function(w, h)
17869     {
17870         
17871         
17872 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17873 //        
17874 //        if(typeof w != 'number'){
17875 //            // we do not handle it!?!?
17876 //            return;
17877 //        }
17878 //        var tw = this.trigger.getWidth();
17879 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17880 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17881 //        var x = w - tw;
17882 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17883 //            
17884 //        //this.trigger.setStyle('left', x+'px');
17885 //        
17886 //        if(this.list && this.listWidth === undefined){
17887 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17888 //            this.list.setWidth(lw);
17889 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17890 //        }
17891         
17892     
17893         
17894     },
17895
17896     /**
17897      * Allow or prevent the user from directly editing the field text.  If false is passed,
17898      * the user will only be able to select from the items defined in the dropdown list.  This method
17899      * is the runtime equivalent of setting the 'editable' config option at config time.
17900      * @param {Boolean} value True to allow the user to directly edit the field text
17901      */
17902     setEditable : function(value){
17903         if(value == this.editable){
17904             return;
17905         }
17906         this.editable = value;
17907         if(!value){
17908             this.inputEl().dom.setAttribute('readOnly', true);
17909             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17910             this.inputEl().addClass('x-combo-noedit');
17911         }else{
17912             this.inputEl().dom.removeAttribute('readOnly');
17913             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17914             this.inputEl().removeClass('x-combo-noedit');
17915         }
17916     },
17917
17918     // private
17919     
17920     onBeforeLoad : function(combo,opts){
17921         if(!this.hasFocus){
17922             return;
17923         }
17924          if (!opts.add) {
17925             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17926          }
17927         this.restrictHeight();
17928         this.selectedIndex = -1;
17929     },
17930
17931     // private
17932     onLoad : function(){
17933         
17934         this.hasQuery = false;
17935         
17936         if(!this.hasFocus){
17937             return;
17938         }
17939         
17940         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17941             this.loading.hide();
17942         }
17943         
17944         if(this.store.getCount() > 0){
17945             
17946             this.expand();
17947             this.restrictHeight();
17948             if(this.lastQuery == this.allQuery){
17949                 if(this.editable && !this.tickable){
17950                     this.inputEl().dom.select();
17951                 }
17952                 
17953                 if(
17954                     !this.selectByValue(this.value, true) &&
17955                     this.autoFocus && 
17956                     (
17957                         !this.store.lastOptions ||
17958                         typeof(this.store.lastOptions.add) == 'undefined' || 
17959                         this.store.lastOptions.add != true
17960                     )
17961                 ){
17962                     this.select(0, true);
17963                 }
17964             }else{
17965                 if(this.autoFocus){
17966                     this.selectNext();
17967                 }
17968                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17969                     this.taTask.delay(this.typeAheadDelay);
17970                 }
17971             }
17972         }else{
17973             this.onEmptyResults();
17974         }
17975         
17976         //this.el.focus();
17977     },
17978     // private
17979     onLoadException : function()
17980     {
17981         this.hasQuery = false;
17982         
17983         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17984             this.loading.hide();
17985         }
17986         
17987         if(this.tickable && this.editable){
17988             return;
17989         }
17990         
17991         this.collapse();
17992         // only causes errors at present
17993         //Roo.log(this.store.reader.jsonData);
17994         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17995             // fixme
17996             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17997         //}
17998         
17999         
18000     },
18001     // private
18002     onTypeAhead : function(){
18003         if(this.store.getCount() > 0){
18004             var r = this.store.getAt(0);
18005             var newValue = r.data[this.displayField];
18006             var len = newValue.length;
18007             var selStart = this.getRawValue().length;
18008             
18009             if(selStart != len){
18010                 this.setRawValue(newValue);
18011                 this.selectText(selStart, newValue.length);
18012             }
18013         }
18014     },
18015
18016     // private
18017     onSelect : function(record, index){
18018         
18019         if(this.fireEvent('beforeselect', this, record, index) !== false){
18020         
18021             this.setFromData(index > -1 ? record.data : false);
18022             
18023             this.collapse();
18024             this.fireEvent('select', this, record, index);
18025         }
18026     },
18027
18028     /**
18029      * Returns the currently selected field value or empty string if no value is set.
18030      * @return {String} value The selected value
18031      */
18032     getValue : function()
18033     {
18034         if(Roo.isIOS && this.useNativeIOS){
18035             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18036         }
18037         
18038         if(this.multiple){
18039             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18040         }
18041         
18042         if(this.valueField){
18043             return typeof this.value != 'undefined' ? this.value : '';
18044         }else{
18045             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18046         }
18047     },
18048     
18049     getRawValue : function()
18050     {
18051         if(Roo.isIOS && this.useNativeIOS){
18052             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18053         }
18054         
18055         var v = this.inputEl().getValue();
18056         
18057         return v;
18058     },
18059
18060     /**
18061      * Clears any text/value currently set in the field
18062      */
18063     clearValue : function(){
18064         
18065         if(this.hiddenField){
18066             this.hiddenField.dom.value = '';
18067         }
18068         this.value = '';
18069         this.setRawValue('');
18070         this.lastSelectionText = '';
18071         this.lastData = false;
18072         
18073         var close = this.closeTriggerEl();
18074         
18075         if(close){
18076             close.hide();
18077         }
18078         
18079         this.validate();
18080         
18081     },
18082
18083     /**
18084      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18085      * will be displayed in the field.  If the value does not match the data value of an existing item,
18086      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18087      * Otherwise the field will be blank (although the value will still be set).
18088      * @param {String} value The value to match
18089      */
18090     setValue : function(v)
18091     {
18092         if(Roo.isIOS && this.useNativeIOS){
18093             this.setIOSValue(v);
18094             return;
18095         }
18096         
18097         if(this.multiple){
18098             this.syncValue();
18099             return;
18100         }
18101         
18102         var text = v;
18103         if(this.valueField){
18104             var r = this.findRecord(this.valueField, v);
18105             if(r){
18106                 text = r.data[this.displayField];
18107             }else if(this.valueNotFoundText !== undefined){
18108                 text = this.valueNotFoundText;
18109             }
18110         }
18111         this.lastSelectionText = text;
18112         if(this.hiddenField){
18113             this.hiddenField.dom.value = v;
18114         }
18115         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18116         this.value = v;
18117         
18118         var close = this.closeTriggerEl();
18119         
18120         if(close){
18121             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18122         }
18123         
18124         this.validate();
18125     },
18126     /**
18127      * @property {Object} the last set data for the element
18128      */
18129     
18130     lastData : false,
18131     /**
18132      * Sets the value of the field based on a object which is related to the record format for the store.
18133      * @param {Object} value the value to set as. or false on reset?
18134      */
18135     setFromData : function(o){
18136         
18137         if(this.multiple){
18138             this.addItem(o);
18139             return;
18140         }
18141             
18142         var dv = ''; // display value
18143         var vv = ''; // value value..
18144         this.lastData = o;
18145         if (this.displayField) {
18146             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18147         } else {
18148             // this is an error condition!!!
18149             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18150         }
18151         
18152         if(this.valueField){
18153             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18154         }
18155         
18156         var close = this.closeTriggerEl();
18157         
18158         if(close){
18159             if(dv.length || vv * 1 > 0){
18160                 close.show() ;
18161                 this.blockFocus=true;
18162             } else {
18163                 close.hide();
18164             }             
18165         }
18166         
18167         if(this.hiddenField){
18168             this.hiddenField.dom.value = vv;
18169             
18170             this.lastSelectionText = dv;
18171             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18172             this.value = vv;
18173             return;
18174         }
18175         // no hidden field.. - we store the value in 'value', but still display
18176         // display field!!!!
18177         this.lastSelectionText = dv;
18178         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18179         this.value = vv;
18180         
18181         
18182         
18183     },
18184     // private
18185     reset : function(){
18186         // overridden so that last data is reset..
18187         
18188         if(this.multiple){
18189             this.clearItem();
18190             return;
18191         }
18192         
18193         this.setValue(this.originalValue);
18194         //this.clearInvalid();
18195         this.lastData = false;
18196         if (this.view) {
18197             this.view.clearSelections();
18198         }
18199         
18200         this.validate();
18201     },
18202     // private
18203     findRecord : function(prop, value){
18204         var record;
18205         if(this.store.getCount() > 0){
18206             this.store.each(function(r){
18207                 if(r.data[prop] == value){
18208                     record = r;
18209                     return false;
18210                 }
18211                 return true;
18212             });
18213         }
18214         return record;
18215     },
18216     
18217     getName: function()
18218     {
18219         // returns hidden if it's set..
18220         if (!this.rendered) {return ''};
18221         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18222         
18223     },
18224     // private
18225     onViewMove : function(e, t){
18226         this.inKeyMode = false;
18227     },
18228
18229     // private
18230     onViewOver : function(e, t){
18231         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18232             return;
18233         }
18234         var item = this.view.findItemFromChild(t);
18235         
18236         if(item){
18237             var index = this.view.indexOf(item);
18238             this.select(index, false);
18239         }
18240     },
18241
18242     // private
18243     onViewClick : function(view, doFocus, el, e)
18244     {
18245         var index = this.view.getSelectedIndexes()[0];
18246         
18247         var r = this.store.getAt(index);
18248         
18249         if(this.tickable){
18250             
18251             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18252                 return;
18253             }
18254             
18255             var rm = false;
18256             var _this = this;
18257             
18258             Roo.each(this.tickItems, function(v,k){
18259                 
18260                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18261                     Roo.log(v);
18262                     _this.tickItems.splice(k, 1);
18263                     
18264                     if(typeof(e) == 'undefined' && view == false){
18265                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18266                     }
18267                     
18268                     rm = true;
18269                     return;
18270                 }
18271             });
18272             
18273             if(rm){
18274                 return;
18275             }
18276             
18277             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18278                 this.tickItems.push(r.data);
18279             }
18280             
18281             if(typeof(e) == 'undefined' && view == false){
18282                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18283             }
18284                     
18285             return;
18286         }
18287         
18288         if(r){
18289             this.onSelect(r, index);
18290         }
18291         if(doFocus !== false && !this.blockFocus){
18292             this.inputEl().focus();
18293         }
18294     },
18295
18296     // private
18297     restrictHeight : function(){
18298         //this.innerList.dom.style.height = '';
18299         //var inner = this.innerList.dom;
18300         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18301         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18302         //this.list.beginUpdate();
18303         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18304         this.list.alignTo(this.inputEl(), this.listAlign);
18305         this.list.alignTo(this.inputEl(), this.listAlign);
18306         //this.list.endUpdate();
18307     },
18308
18309     // private
18310     onEmptyResults : function(){
18311         
18312         if(this.tickable && this.editable){
18313             this.hasFocus = false;
18314             this.restrictHeight();
18315             return;
18316         }
18317         
18318         this.collapse();
18319     },
18320
18321     /**
18322      * Returns true if the dropdown list is expanded, else false.
18323      */
18324     isExpanded : function(){
18325         return this.list.isVisible();
18326     },
18327
18328     /**
18329      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18330      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18331      * @param {String} value The data value of the item to select
18332      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18333      * selected item if it is not currently in view (defaults to true)
18334      * @return {Boolean} True if the value matched an item in the list, else false
18335      */
18336     selectByValue : function(v, scrollIntoView){
18337         if(v !== undefined && v !== null){
18338             var r = this.findRecord(this.valueField || this.displayField, v);
18339             if(r){
18340                 this.select(this.store.indexOf(r), scrollIntoView);
18341                 return true;
18342             }
18343         }
18344         return false;
18345     },
18346
18347     /**
18348      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18349      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18350      * @param {Number} index The zero-based index of the list item to select
18351      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18352      * selected item if it is not currently in view (defaults to true)
18353      */
18354     select : function(index, scrollIntoView){
18355         this.selectedIndex = index;
18356         this.view.select(index);
18357         if(scrollIntoView !== false){
18358             var el = this.view.getNode(index);
18359             /*
18360              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18361              */
18362             if(el){
18363                 this.list.scrollChildIntoView(el, false);
18364             }
18365         }
18366     },
18367
18368     // private
18369     selectNext : function(){
18370         var ct = this.store.getCount();
18371         if(ct > 0){
18372             if(this.selectedIndex == -1){
18373                 this.select(0);
18374             }else if(this.selectedIndex < ct-1){
18375                 this.select(this.selectedIndex+1);
18376             }
18377         }
18378     },
18379
18380     // private
18381     selectPrev : function(){
18382         var ct = this.store.getCount();
18383         if(ct > 0){
18384             if(this.selectedIndex == -1){
18385                 this.select(0);
18386             }else if(this.selectedIndex != 0){
18387                 this.select(this.selectedIndex-1);
18388             }
18389         }
18390     },
18391
18392     // private
18393     onKeyUp : function(e){
18394         if(this.editable !== false && !e.isSpecialKey()){
18395             this.lastKey = e.getKey();
18396             this.dqTask.delay(this.queryDelay);
18397         }
18398     },
18399
18400     // private
18401     validateBlur : function(){
18402         return !this.list || !this.list.isVisible();   
18403     },
18404
18405     // private
18406     initQuery : function(){
18407         
18408         var v = this.getRawValue();
18409         
18410         if(this.tickable && this.editable){
18411             v = this.tickableInputEl().getValue();
18412         }
18413         
18414         this.doQuery(v);
18415     },
18416
18417     // private
18418     doForce : function(){
18419         if(this.inputEl().dom.value.length > 0){
18420             this.inputEl().dom.value =
18421                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18422              
18423         }
18424     },
18425
18426     /**
18427      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18428      * query allowing the query action to be canceled if needed.
18429      * @param {String} query The SQL query to execute
18430      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18431      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18432      * saved in the current store (defaults to false)
18433      */
18434     doQuery : function(q, forceAll){
18435         
18436         if(q === undefined || q === null){
18437             q = '';
18438         }
18439         var qe = {
18440             query: q,
18441             forceAll: forceAll,
18442             combo: this,
18443             cancel:false
18444         };
18445         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18446             return false;
18447         }
18448         q = qe.query;
18449         
18450         forceAll = qe.forceAll;
18451         if(forceAll === true || (q.length >= this.minChars)){
18452             
18453             this.hasQuery = true;
18454             
18455             if(this.lastQuery != q || this.alwaysQuery){
18456                 this.lastQuery = q;
18457                 if(this.mode == 'local'){
18458                     this.selectedIndex = -1;
18459                     if(forceAll){
18460                         this.store.clearFilter();
18461                     }else{
18462                         
18463                         if(this.specialFilter){
18464                             this.fireEvent('specialfilter', this);
18465                             this.onLoad();
18466                             return;
18467                         }
18468                         
18469                         this.store.filter(this.displayField, q);
18470                     }
18471                     
18472                     this.store.fireEvent("datachanged", this.store);
18473                     
18474                     this.onLoad();
18475                     
18476                     
18477                 }else{
18478                     
18479                     this.store.baseParams[this.queryParam] = q;
18480                     
18481                     var options = {params : this.getParams(q)};
18482                     
18483                     if(this.loadNext){
18484                         options.add = true;
18485                         options.params.start = this.page * this.pageSize;
18486                     }
18487                     
18488                     this.store.load(options);
18489                     
18490                     /*
18491                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18492                      *  we should expand the list on onLoad
18493                      *  so command out it
18494                      */
18495 //                    this.expand();
18496                 }
18497             }else{
18498                 this.selectedIndex = -1;
18499                 this.onLoad();   
18500             }
18501         }
18502         
18503         this.loadNext = false;
18504     },
18505     
18506     // private
18507     getParams : function(q){
18508         var p = {};
18509         //p[this.queryParam] = q;
18510         
18511         if(this.pageSize){
18512             p.start = 0;
18513             p.limit = this.pageSize;
18514         }
18515         return p;
18516     },
18517
18518     /**
18519      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18520      */
18521     collapse : function(){
18522         if(!this.isExpanded()){
18523             return;
18524         }
18525         
18526         this.list.hide();
18527         
18528         this.hasFocus = false;
18529         
18530         if(this.tickable){
18531             this.okBtn.hide();
18532             this.cancelBtn.hide();
18533             this.trigger.show();
18534             
18535             if(this.editable){
18536                 this.tickableInputEl().dom.value = '';
18537                 this.tickableInputEl().blur();
18538             }
18539             
18540         }
18541         
18542         Roo.get(document).un('mousedown', this.collapseIf, this);
18543         Roo.get(document).un('mousewheel', this.collapseIf, this);
18544         if (!this.editable) {
18545             Roo.get(document).un('keydown', this.listKeyPress, this);
18546         }
18547         this.fireEvent('collapse', this);
18548         
18549         this.validate();
18550     },
18551
18552     // private
18553     collapseIf : function(e){
18554         var in_combo  = e.within(this.el);
18555         var in_list =  e.within(this.list);
18556         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18557         
18558         if (in_combo || in_list || is_list) {
18559             //e.stopPropagation();
18560             return;
18561         }
18562         
18563         if(this.tickable){
18564             this.onTickableFooterButtonClick(e, false, false);
18565         }
18566
18567         this.collapse();
18568         
18569     },
18570
18571     /**
18572      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18573      */
18574     expand : function(){
18575        
18576         if(this.isExpanded() || !this.hasFocus){
18577             return;
18578         }
18579         
18580         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18581         this.list.setWidth(lw);
18582         
18583         Roo.log('expand');
18584         
18585         this.list.show();
18586         
18587         this.restrictHeight();
18588         
18589         if(this.tickable){
18590             
18591             this.tickItems = Roo.apply([], this.item);
18592             
18593             this.okBtn.show();
18594             this.cancelBtn.show();
18595             this.trigger.hide();
18596             
18597             if(this.editable){
18598                 this.tickableInputEl().focus();
18599             }
18600             
18601         }
18602         
18603         Roo.get(document).on('mousedown', this.collapseIf, this);
18604         Roo.get(document).on('mousewheel', this.collapseIf, this);
18605         if (!this.editable) {
18606             Roo.get(document).on('keydown', this.listKeyPress, this);
18607         }
18608         
18609         this.fireEvent('expand', this);
18610     },
18611
18612     // private
18613     // Implements the default empty TriggerField.onTriggerClick function
18614     onTriggerClick : function(e)
18615     {
18616         Roo.log('trigger click');
18617         
18618         if(this.disabled || !this.triggerList){
18619             return;
18620         }
18621         
18622         this.page = 0;
18623         this.loadNext = false;
18624         
18625         if(this.isExpanded()){
18626             this.collapse();
18627             if (!this.blockFocus) {
18628                 this.inputEl().focus();
18629             }
18630             
18631         }else {
18632             this.hasFocus = true;
18633             if(this.triggerAction == 'all') {
18634                 this.doQuery(this.allQuery, true);
18635             } else {
18636                 this.doQuery(this.getRawValue());
18637             }
18638             if (!this.blockFocus) {
18639                 this.inputEl().focus();
18640             }
18641         }
18642     },
18643     
18644     onTickableTriggerClick : function(e)
18645     {
18646         if(this.disabled){
18647             return;
18648         }
18649         
18650         this.page = 0;
18651         this.loadNext = false;
18652         this.hasFocus = true;
18653         
18654         if(this.triggerAction == 'all') {
18655             this.doQuery(this.allQuery, true);
18656         } else {
18657             this.doQuery(this.getRawValue());
18658         }
18659     },
18660     
18661     onSearchFieldClick : function(e)
18662     {
18663         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18664             this.onTickableFooterButtonClick(e, false, false);
18665             return;
18666         }
18667         
18668         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18669             return;
18670         }
18671         
18672         this.page = 0;
18673         this.loadNext = false;
18674         this.hasFocus = true;
18675         
18676         if(this.triggerAction == 'all') {
18677             this.doQuery(this.allQuery, true);
18678         } else {
18679             this.doQuery(this.getRawValue());
18680         }
18681     },
18682     
18683     listKeyPress : function(e)
18684     {
18685         //Roo.log('listkeypress');
18686         // scroll to first matching element based on key pres..
18687         if (e.isSpecialKey()) {
18688             return false;
18689         }
18690         var k = String.fromCharCode(e.getKey()).toUpperCase();
18691         //Roo.log(k);
18692         var match  = false;
18693         var csel = this.view.getSelectedNodes();
18694         var cselitem = false;
18695         if (csel.length) {
18696             var ix = this.view.indexOf(csel[0]);
18697             cselitem  = this.store.getAt(ix);
18698             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18699                 cselitem = false;
18700             }
18701             
18702         }
18703         
18704         this.store.each(function(v) { 
18705             if (cselitem) {
18706                 // start at existing selection.
18707                 if (cselitem.id == v.id) {
18708                     cselitem = false;
18709                 }
18710                 return true;
18711             }
18712                 
18713             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18714                 match = this.store.indexOf(v);
18715                 return false;
18716             }
18717             return true;
18718         }, this);
18719         
18720         if (match === false) {
18721             return true; // no more action?
18722         }
18723         // scroll to?
18724         this.view.select(match);
18725         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18726         sn.scrollIntoView(sn.dom.parentNode, false);
18727     },
18728     
18729     onViewScroll : function(e, t){
18730         
18731         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){
18732             return;
18733         }
18734         
18735         this.hasQuery = true;
18736         
18737         this.loading = this.list.select('.loading', true).first();
18738         
18739         if(this.loading === null){
18740             this.list.createChild({
18741                 tag: 'div',
18742                 cls: 'loading roo-select2-more-results roo-select2-active',
18743                 html: 'Loading more results...'
18744             });
18745             
18746             this.loading = this.list.select('.loading', true).first();
18747             
18748             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18749             
18750             this.loading.hide();
18751         }
18752         
18753         this.loading.show();
18754         
18755         var _combo = this;
18756         
18757         this.page++;
18758         this.loadNext = true;
18759         
18760         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18761         
18762         return;
18763     },
18764     
18765     addItem : function(o)
18766     {   
18767         var dv = ''; // display value
18768         
18769         if (this.displayField) {
18770             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18771         } else {
18772             // this is an error condition!!!
18773             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18774         }
18775         
18776         if(!dv.length){
18777             return;
18778         }
18779         
18780         var choice = this.choices.createChild({
18781             tag: 'li',
18782             cls: 'roo-select2-search-choice',
18783             cn: [
18784                 {
18785                     tag: 'div',
18786                     html: dv
18787                 },
18788                 {
18789                     tag: 'a',
18790                     href: '#',
18791                     cls: 'roo-select2-search-choice-close fa fa-times',
18792                     tabindex: '-1'
18793                 }
18794             ]
18795             
18796         }, this.searchField);
18797         
18798         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18799         
18800         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18801         
18802         this.item.push(o);
18803         
18804         this.lastData = o;
18805         
18806         this.syncValue();
18807         
18808         this.inputEl().dom.value = '';
18809         
18810         this.validate();
18811     },
18812     
18813     onRemoveItem : function(e, _self, o)
18814     {
18815         e.preventDefault();
18816         
18817         this.lastItem = Roo.apply([], this.item);
18818         
18819         var index = this.item.indexOf(o.data) * 1;
18820         
18821         if( index < 0){
18822             Roo.log('not this item?!');
18823             return;
18824         }
18825         
18826         this.item.splice(index, 1);
18827         o.item.remove();
18828         
18829         this.syncValue();
18830         
18831         this.fireEvent('remove', this, e);
18832         
18833         this.validate();
18834         
18835     },
18836     
18837     syncValue : function()
18838     {
18839         if(!this.item.length){
18840             this.clearValue();
18841             return;
18842         }
18843             
18844         var value = [];
18845         var _this = this;
18846         Roo.each(this.item, function(i){
18847             if(_this.valueField){
18848                 value.push(i[_this.valueField]);
18849                 return;
18850             }
18851
18852             value.push(i);
18853         });
18854
18855         this.value = value.join(',');
18856
18857         if(this.hiddenField){
18858             this.hiddenField.dom.value = this.value;
18859         }
18860         
18861         this.store.fireEvent("datachanged", this.store);
18862         
18863         this.validate();
18864     },
18865     
18866     clearItem : function()
18867     {
18868         if(!this.multiple){
18869             return;
18870         }
18871         
18872         this.item = [];
18873         
18874         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18875            c.remove();
18876         });
18877         
18878         this.syncValue();
18879         
18880         this.validate();
18881         
18882         if(this.tickable && !Roo.isTouch){
18883             this.view.refresh();
18884         }
18885     },
18886     
18887     inputEl: function ()
18888     {
18889         if(Roo.isIOS && this.useNativeIOS){
18890             return this.el.select('select.roo-ios-select', true).first();
18891         }
18892         
18893         if(Roo.isTouch && this.mobileTouchView){
18894             return this.el.select('input.form-control',true).first();
18895         }
18896         
18897         if(this.tickable){
18898             return this.searchField;
18899         }
18900         
18901         return this.el.select('input.form-control',true).first();
18902     },
18903     
18904     onTickableFooterButtonClick : function(e, btn, el)
18905     {
18906         e.preventDefault();
18907         
18908         this.lastItem = Roo.apply([], this.item);
18909         
18910         if(btn && btn.name == 'cancel'){
18911             this.tickItems = Roo.apply([], this.item);
18912             this.collapse();
18913             return;
18914         }
18915         
18916         this.clearItem();
18917         
18918         var _this = this;
18919         
18920         Roo.each(this.tickItems, function(o){
18921             _this.addItem(o);
18922         });
18923         
18924         this.collapse();
18925         
18926     },
18927     
18928     validate : function()
18929     {
18930         if(this.getVisibilityEl().hasClass('hidden')){
18931             return true;
18932         }
18933         
18934         var v = this.getRawValue();
18935         
18936         if(this.multiple){
18937             v = this.getValue();
18938         }
18939         
18940         if(this.disabled || this.allowBlank || v.length){
18941             this.markValid();
18942             return true;
18943         }
18944         
18945         this.markInvalid();
18946         return false;
18947     },
18948     
18949     tickableInputEl : function()
18950     {
18951         if(!this.tickable || !this.editable){
18952             return this.inputEl();
18953         }
18954         
18955         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18956     },
18957     
18958     
18959     getAutoCreateTouchView : function()
18960     {
18961         var id = Roo.id();
18962         
18963         var cfg = {
18964             cls: 'form-group' //input-group
18965         };
18966         
18967         var input =  {
18968             tag: 'input',
18969             id : id,
18970             type : this.inputType,
18971             cls : 'form-control x-combo-noedit',
18972             autocomplete: 'new-password',
18973             placeholder : this.placeholder || '',
18974             readonly : true
18975         };
18976         
18977         if (this.name) {
18978             input.name = this.name;
18979         }
18980         
18981         if (this.size) {
18982             input.cls += ' input-' + this.size;
18983         }
18984         
18985         if (this.disabled) {
18986             input.disabled = true;
18987         }
18988         
18989         var inputblock = {
18990             cls : 'roo-combobox-wrap',
18991             cn : [
18992                 input
18993             ]
18994         };
18995         
18996         if(this.before){
18997             inputblock.cls += ' input-group';
18998             
18999             inputblock.cn.unshift({
19000                 tag :'span',
19001                 cls : 'input-group-addon input-group-prepend input-group-text',
19002                 html : this.before
19003             });
19004         }
19005         
19006         if(this.removable && !this.multiple){
19007             inputblock.cls += ' roo-removable';
19008             
19009             inputblock.cn.push({
19010                 tag: 'button',
19011                 html : 'x',
19012                 cls : 'roo-combo-removable-btn close'
19013             });
19014         }
19015
19016         if(this.hasFeedback && !this.allowBlank){
19017             
19018             inputblock.cls += ' has-feedback';
19019             
19020             inputblock.cn.push({
19021                 tag: 'span',
19022                 cls: 'glyphicon form-control-feedback'
19023             });
19024             
19025         }
19026         
19027         if (this.after) {
19028             
19029             inputblock.cls += (this.before) ? '' : ' input-group';
19030             
19031             inputblock.cn.push({
19032                 tag :'span',
19033                 cls : 'input-group-addon input-group-append input-group-text',
19034                 html : this.after
19035             });
19036         }
19037
19038         
19039         var ibwrap = inputblock;
19040         
19041         if(this.multiple){
19042             ibwrap = {
19043                 tag: 'ul',
19044                 cls: 'roo-select2-choices',
19045                 cn:[
19046                     {
19047                         tag: 'li',
19048                         cls: 'roo-select2-search-field',
19049                         cn: [
19050
19051                             inputblock
19052                         ]
19053                     }
19054                 ]
19055             };
19056         
19057             
19058         }
19059         
19060         var combobox = {
19061             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19062             cn: [
19063                 {
19064                     tag: 'input',
19065                     type : 'hidden',
19066                     cls: 'form-hidden-field'
19067                 },
19068                 ibwrap
19069             ]
19070         };
19071         
19072         if(!this.multiple && this.showToggleBtn){
19073             
19074             var caret = {
19075                 cls: 'caret'
19076             };
19077             
19078             if (this.caret != false) {
19079                 caret = {
19080                      tag: 'i',
19081                      cls: 'fa fa-' + this.caret
19082                 };
19083                 
19084             }
19085             
19086             combobox.cn.push({
19087                 tag :'span',
19088                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19089                 cn : [
19090                     Roo.bootstrap.version == 3 ? caret : '',
19091                     {
19092                         tag: 'span',
19093                         cls: 'combobox-clear',
19094                         cn  : [
19095                             {
19096                                 tag : 'i',
19097                                 cls: 'icon-remove'
19098                             }
19099                         ]
19100                     }
19101                 ]
19102
19103             })
19104         }
19105         
19106         if(this.multiple){
19107             combobox.cls += ' roo-select2-container-multi';
19108         }
19109         
19110         var required =  this.allowBlank ?  {
19111                     tag : 'i',
19112                     style: 'display: none'
19113                 } : {
19114                    tag : 'i',
19115                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19116                    tooltip : 'This field is required'
19117                 };
19118         
19119         var align = this.labelAlign || this.parentLabelAlign();
19120         
19121         if (align ==='left' && this.fieldLabel.length) {
19122
19123             cfg.cn = [
19124                 required,
19125                 {
19126                     tag: 'label',
19127                     cls : 'control-label col-form-label',
19128                     html : this.fieldLabel
19129
19130                 },
19131                 {
19132                     cls : 'roo-combobox-wrap ', 
19133                     cn: [
19134                         combobox
19135                     ]
19136                 }
19137             ];
19138             
19139             var labelCfg = cfg.cn[1];
19140             var contentCfg = cfg.cn[2];
19141             
19142
19143             if(this.indicatorpos == 'right'){
19144                 cfg.cn = [
19145                     {
19146                         tag: 'label',
19147                         'for' :  id,
19148                         cls : 'control-label col-form-label',
19149                         cn : [
19150                             {
19151                                 tag : 'span',
19152                                 html : this.fieldLabel
19153                             },
19154                             required
19155                         ]
19156                     },
19157                     {
19158                         cls : "roo-combobox-wrap ",
19159                         cn: [
19160                             combobox
19161                         ]
19162                     }
19163
19164                 ];
19165                 
19166                 labelCfg = cfg.cn[0];
19167                 contentCfg = cfg.cn[1];
19168             }
19169             
19170            
19171             
19172             if(this.labelWidth > 12){
19173                 labelCfg.style = "width: " + this.labelWidth + 'px';
19174             }
19175            
19176             if(this.labelWidth < 13 && this.labelmd == 0){
19177                 this.labelmd = this.labelWidth;
19178             }
19179             
19180             if(this.labellg > 0){
19181                 labelCfg.cls += ' col-lg-' + this.labellg;
19182                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19183             }
19184             
19185             if(this.labelmd > 0){
19186                 labelCfg.cls += ' col-md-' + this.labelmd;
19187                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19188             }
19189             
19190             if(this.labelsm > 0){
19191                 labelCfg.cls += ' col-sm-' + this.labelsm;
19192                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19193             }
19194             
19195             if(this.labelxs > 0){
19196                 labelCfg.cls += ' col-xs-' + this.labelxs;
19197                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19198             }
19199                 
19200                 
19201         } else if ( this.fieldLabel.length) {
19202             cfg.cn = [
19203                required,
19204                 {
19205                     tag: 'label',
19206                     cls : 'control-label',
19207                     html : this.fieldLabel
19208
19209                 },
19210                 {
19211                     cls : '', 
19212                     cn: [
19213                         combobox
19214                     ]
19215                 }
19216             ];
19217             
19218             if(this.indicatorpos == 'right'){
19219                 cfg.cn = [
19220                     {
19221                         tag: 'label',
19222                         cls : 'control-label',
19223                         html : this.fieldLabel,
19224                         cn : [
19225                             required
19226                         ]
19227                     },
19228                     {
19229                         cls : '', 
19230                         cn: [
19231                             combobox
19232                         ]
19233                     }
19234                 ];
19235             }
19236         } else {
19237             cfg.cn = combobox;    
19238         }
19239         
19240         
19241         var settings = this;
19242         
19243         ['xs','sm','md','lg'].map(function(size){
19244             if (settings[size]) {
19245                 cfg.cls += ' col-' + size + '-' + settings[size];
19246             }
19247         });
19248         
19249         return cfg;
19250     },
19251     
19252     initTouchView : function()
19253     {
19254         this.renderTouchView();
19255         
19256         this.touchViewEl.on('scroll', function(){
19257             this.el.dom.scrollTop = 0;
19258         }, this);
19259         
19260         this.originalValue = this.getValue();
19261         
19262         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19263         
19264         this.inputEl().on("click", this.showTouchView, this);
19265         if (this.triggerEl) {
19266             this.triggerEl.on("click", this.showTouchView, this);
19267         }
19268         
19269         
19270         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19271         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19272         
19273         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19274         
19275         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19276         this.store.on('load', this.onTouchViewLoad, this);
19277         this.store.on('loadexception', this.onTouchViewLoadException, this);
19278         
19279         if(this.hiddenName){
19280             
19281             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19282             
19283             this.hiddenField.dom.value =
19284                 this.hiddenValue !== undefined ? this.hiddenValue :
19285                 this.value !== undefined ? this.value : '';
19286         
19287             this.el.dom.removeAttribute('name');
19288             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19289         }
19290         
19291         if(this.multiple){
19292             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19293             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19294         }
19295         
19296         if(this.removable && !this.multiple){
19297             var close = this.closeTriggerEl();
19298             if(close){
19299                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19300                 close.on('click', this.removeBtnClick, this, close);
19301             }
19302         }
19303         /*
19304          * fix the bug in Safari iOS8
19305          */
19306         this.inputEl().on("focus", function(e){
19307             document.activeElement.blur();
19308         }, this);
19309         
19310         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19311         
19312         return;
19313         
19314         
19315     },
19316     
19317     renderTouchView : function()
19318     {
19319         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19320         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19321         
19322         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19323         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19324         
19325         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19326         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327         this.touchViewBodyEl.setStyle('overflow', 'auto');
19328         
19329         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19330         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19331         
19332         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19333         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19334         
19335     },
19336     
19337     showTouchView : function()
19338     {
19339         if(this.disabled){
19340             return;
19341         }
19342         
19343         this.touchViewHeaderEl.hide();
19344
19345         if(this.modalTitle.length){
19346             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19347             this.touchViewHeaderEl.show();
19348         }
19349
19350         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19351         this.touchViewEl.show();
19352
19353         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19354         
19355         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19356         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19357
19358         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19359
19360         if(this.modalTitle.length){
19361             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19362         }
19363         
19364         this.touchViewBodyEl.setHeight(bodyHeight);
19365
19366         if(this.animate){
19367             var _this = this;
19368             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19369         }else{
19370             this.touchViewEl.addClass(['in','show']);
19371         }
19372         
19373         if(this._touchViewMask){
19374             Roo.get(document.body).addClass("x-body-masked");
19375             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19376             this._touchViewMask.setStyle('z-index', 10000);
19377             this._touchViewMask.addClass('show');
19378         }
19379         
19380         this.doTouchViewQuery();
19381         
19382     },
19383     
19384     hideTouchView : function()
19385     {
19386         this.touchViewEl.removeClass(['in','show']);
19387
19388         if(this.animate){
19389             var _this = this;
19390             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19391         }else{
19392             this.touchViewEl.setStyle('display', 'none');
19393         }
19394         
19395         if(this._touchViewMask){
19396             this._touchViewMask.removeClass('show');
19397             Roo.get(document.body).removeClass("x-body-masked");
19398         }
19399     },
19400     
19401     setTouchViewValue : function()
19402     {
19403         if(this.multiple){
19404             this.clearItem();
19405         
19406             var _this = this;
19407
19408             Roo.each(this.tickItems, function(o){
19409                 this.addItem(o);
19410             }, this);
19411         }
19412         
19413         this.hideTouchView();
19414     },
19415     
19416     doTouchViewQuery : function()
19417     {
19418         var qe = {
19419             query: '',
19420             forceAll: true,
19421             combo: this,
19422             cancel:false
19423         };
19424         
19425         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19426             return false;
19427         }
19428         
19429         if(!this.alwaysQuery || this.mode == 'local'){
19430             this.onTouchViewLoad();
19431             return;
19432         }
19433         
19434         this.store.load();
19435     },
19436     
19437     onTouchViewBeforeLoad : function(combo,opts)
19438     {
19439         return;
19440     },
19441
19442     // private
19443     onTouchViewLoad : function()
19444     {
19445         if(this.store.getCount() < 1){
19446             this.onTouchViewEmptyResults();
19447             return;
19448         }
19449         
19450         this.clearTouchView();
19451         
19452         var rawValue = this.getRawValue();
19453         
19454         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19455         
19456         this.tickItems = [];
19457         
19458         this.store.data.each(function(d, rowIndex){
19459             var row = this.touchViewListGroup.createChild(template);
19460             
19461             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19462                 row.addClass(d.data.cls);
19463             }
19464             
19465             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19466                 var cfg = {
19467                     data : d.data,
19468                     html : d.data[this.displayField]
19469                 };
19470                 
19471                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19472                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19473                 }
19474             }
19475             row.removeClass('selected');
19476             if(!this.multiple && this.valueField &&
19477                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19478             {
19479                 // radio buttons..
19480                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19481                 row.addClass('selected');
19482             }
19483             
19484             if(this.multiple && this.valueField &&
19485                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19486             {
19487                 
19488                 // checkboxes...
19489                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19490                 this.tickItems.push(d.data);
19491             }
19492             
19493             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19494             
19495         }, this);
19496         
19497         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19498         
19499         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19500
19501         if(this.modalTitle.length){
19502             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19503         }
19504
19505         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19506         
19507         if(this.mobile_restrict_height && listHeight < bodyHeight){
19508             this.touchViewBodyEl.setHeight(listHeight);
19509         }
19510         
19511         var _this = this;
19512         
19513         if(firstChecked && listHeight > bodyHeight){
19514             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19515         }
19516         
19517     },
19518     
19519     onTouchViewLoadException : function()
19520     {
19521         this.hideTouchView();
19522     },
19523     
19524     onTouchViewEmptyResults : function()
19525     {
19526         this.clearTouchView();
19527         
19528         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19529         
19530         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19531         
19532     },
19533     
19534     clearTouchView : function()
19535     {
19536         this.touchViewListGroup.dom.innerHTML = '';
19537     },
19538     
19539     onTouchViewClick : function(e, el, o)
19540     {
19541         e.preventDefault();
19542         
19543         var row = o.row;
19544         var rowIndex = o.rowIndex;
19545         
19546         var r = this.store.getAt(rowIndex);
19547         
19548         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19549             
19550             if(!this.multiple){
19551                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19552                     c.dom.removeAttribute('checked');
19553                 }, this);
19554
19555                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19556
19557                 this.setFromData(r.data);
19558
19559                 var close = this.closeTriggerEl();
19560
19561                 if(close){
19562                     close.show();
19563                 }
19564
19565                 this.hideTouchView();
19566
19567                 this.fireEvent('select', this, r, rowIndex);
19568
19569                 return;
19570             }
19571
19572             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19573                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19574                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19575                 return;
19576             }
19577
19578             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19579             this.addItem(r.data);
19580             this.tickItems.push(r.data);
19581         }
19582     },
19583     
19584     getAutoCreateNativeIOS : function()
19585     {
19586         var cfg = {
19587             cls: 'form-group' //input-group,
19588         };
19589         
19590         var combobox =  {
19591             tag: 'select',
19592             cls : 'roo-ios-select'
19593         };
19594         
19595         if (this.name) {
19596             combobox.name = this.name;
19597         }
19598         
19599         if (this.disabled) {
19600             combobox.disabled = true;
19601         }
19602         
19603         var settings = this;
19604         
19605         ['xs','sm','md','lg'].map(function(size){
19606             if (settings[size]) {
19607                 cfg.cls += ' col-' + size + '-' + settings[size];
19608             }
19609         });
19610         
19611         cfg.cn = combobox;
19612         
19613         return cfg;
19614         
19615     },
19616     
19617     initIOSView : function()
19618     {
19619         this.store.on('load', this.onIOSViewLoad, this);
19620         
19621         return;
19622     },
19623     
19624     onIOSViewLoad : function()
19625     {
19626         if(this.store.getCount() < 1){
19627             return;
19628         }
19629         
19630         this.clearIOSView();
19631         
19632         if(this.allowBlank) {
19633             
19634             var default_text = '-- SELECT --';
19635             
19636             if(this.placeholder.length){
19637                 default_text = this.placeholder;
19638             }
19639             
19640             if(this.emptyTitle.length){
19641                 default_text += ' - ' + this.emptyTitle + ' -';
19642             }
19643             
19644             var opt = this.inputEl().createChild({
19645                 tag: 'option',
19646                 value : 0,
19647                 html : default_text
19648             });
19649             
19650             var o = {};
19651             o[this.valueField] = 0;
19652             o[this.displayField] = default_text;
19653             
19654             this.ios_options.push({
19655                 data : o,
19656                 el : opt
19657             });
19658             
19659         }
19660         
19661         this.store.data.each(function(d, rowIndex){
19662             
19663             var html = '';
19664             
19665             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19666                 html = d.data[this.displayField];
19667             }
19668             
19669             var value = '';
19670             
19671             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19672                 value = d.data[this.valueField];
19673             }
19674             
19675             var option = {
19676                 tag: 'option',
19677                 value : value,
19678                 html : html
19679             };
19680             
19681             if(this.value == d.data[this.valueField]){
19682                 option['selected'] = true;
19683             }
19684             
19685             var opt = this.inputEl().createChild(option);
19686             
19687             this.ios_options.push({
19688                 data : d.data,
19689                 el : opt
19690             });
19691             
19692         }, this);
19693         
19694         this.inputEl().on('change', function(){
19695            this.fireEvent('select', this);
19696         }, this);
19697         
19698     },
19699     
19700     clearIOSView: function()
19701     {
19702         this.inputEl().dom.innerHTML = '';
19703         
19704         this.ios_options = [];
19705     },
19706     
19707     setIOSValue: function(v)
19708     {
19709         this.value = v;
19710         
19711         if(!this.ios_options){
19712             return;
19713         }
19714         
19715         Roo.each(this.ios_options, function(opts){
19716            
19717            opts.el.dom.removeAttribute('selected');
19718            
19719            if(opts.data[this.valueField] != v){
19720                return;
19721            }
19722            
19723            opts.el.dom.setAttribute('selected', true);
19724            
19725         }, this);
19726     }
19727
19728     /** 
19729     * @cfg {Boolean} grow 
19730     * @hide 
19731     */
19732     /** 
19733     * @cfg {Number} growMin 
19734     * @hide 
19735     */
19736     /** 
19737     * @cfg {Number} growMax 
19738     * @hide 
19739     */
19740     /**
19741      * @hide
19742      * @method autoSize
19743      */
19744 });
19745
19746 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19747     
19748     header : {
19749         tag: 'div',
19750         cls: 'modal-header',
19751         cn: [
19752             {
19753                 tag: 'h4',
19754                 cls: 'modal-title'
19755             }
19756         ]
19757     },
19758     
19759     body : {
19760         tag: 'div',
19761         cls: 'modal-body',
19762         cn: [
19763             {
19764                 tag: 'ul',
19765                 cls: 'list-group'
19766             }
19767         ]
19768     },
19769     
19770     listItemRadio : {
19771         tag: 'li',
19772         cls: 'list-group-item',
19773         cn: [
19774             {
19775                 tag: 'span',
19776                 cls: 'roo-combobox-list-group-item-value'
19777             },
19778             {
19779                 tag: 'div',
19780                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19781                 cn: [
19782                     {
19783                         tag: 'input',
19784                         type: 'radio'
19785                     },
19786                     {
19787                         tag: 'label'
19788                     }
19789                 ]
19790             }
19791         ]
19792     },
19793     
19794     listItemCheckbox : {
19795         tag: 'li',
19796         cls: 'list-group-item',
19797         cn: [
19798             {
19799                 tag: 'span',
19800                 cls: 'roo-combobox-list-group-item-value'
19801             },
19802             {
19803                 tag: 'div',
19804                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19805                 cn: [
19806                     {
19807                         tag: 'input',
19808                         type: 'checkbox'
19809                     },
19810                     {
19811                         tag: 'label'
19812                     }
19813                 ]
19814             }
19815         ]
19816     },
19817     
19818     emptyResult : {
19819         tag: 'div',
19820         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19821     },
19822     
19823     footer : {
19824         tag: 'div',
19825         cls: 'modal-footer',
19826         cn: [
19827             {
19828                 tag: 'div',
19829                 cls: 'row',
19830                 cn: [
19831                     {
19832                         tag: 'div',
19833                         cls: 'col-xs-6 text-left',
19834                         cn: {
19835                             tag: 'button',
19836                             cls: 'btn btn-danger roo-touch-view-cancel',
19837                             html: 'Cancel'
19838                         }
19839                     },
19840                     {
19841                         tag: 'div',
19842                         cls: 'col-xs-6 text-right',
19843                         cn: {
19844                             tag: 'button',
19845                             cls: 'btn btn-success roo-touch-view-ok',
19846                             html: 'OK'
19847                         }
19848                     }
19849                 ]
19850             }
19851         ]
19852         
19853     }
19854 });
19855
19856 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19857     
19858     touchViewTemplate : {
19859         tag: 'div',
19860         cls: 'modal fade roo-combobox-touch-view',
19861         cn: [
19862             {
19863                 tag: 'div',
19864                 cls: 'modal-dialog',
19865                 style : 'position:fixed', // we have to fix position....
19866                 cn: [
19867                     {
19868                         tag: 'div',
19869                         cls: 'modal-content',
19870                         cn: [
19871                             Roo.bootstrap.form.ComboBox.header,
19872                             Roo.bootstrap.form.ComboBox.body,
19873                             Roo.bootstrap.form.ComboBox.footer
19874                         ]
19875                     }
19876                 ]
19877             }
19878         ]
19879     }
19880 });/*
19881  * Based on:
19882  * Ext JS Library 1.1.1
19883  * Copyright(c) 2006-2007, Ext JS, LLC.
19884  *
19885  * Originally Released Under LGPL - original licence link has changed is not relivant.
19886  *
19887  * Fork - LGPL
19888  * <script type="text/javascript">
19889  */
19890
19891 /**
19892  * @class Roo.View
19893  * @extends Roo.util.Observable
19894  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19895  * This class also supports single and multi selection modes. <br>
19896  * Create a data model bound view:
19897  <pre><code>
19898  var store = new Roo.data.Store(...);
19899
19900  var view = new Roo.View({
19901     el : "my-element",
19902     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19903  
19904     singleSelect: true,
19905     selectedClass: "ydataview-selected",
19906     store: store
19907  });
19908
19909  // listen for node click?
19910  view.on("click", function(vw, index, node, e){
19911  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19912  });
19913
19914  // load XML data
19915  dataModel.load("foobar.xml");
19916  </code></pre>
19917  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19918  * <br><br>
19919  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19920  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19921  * 
19922  * Note: old style constructor is still suported (container, template, config)
19923  * 
19924  * @constructor
19925  * Create a new View
19926  * @param {Object} config The config object
19927  * 
19928  */
19929 Roo.View = function(config, depreciated_tpl, depreciated_config){
19930     
19931     this.parent = false;
19932     
19933     if (typeof(depreciated_tpl) == 'undefined') {
19934         // new way.. - universal constructor.
19935         Roo.apply(this, config);
19936         this.el  = Roo.get(this.el);
19937     } else {
19938         // old format..
19939         this.el  = Roo.get(config);
19940         this.tpl = depreciated_tpl;
19941         Roo.apply(this, depreciated_config);
19942     }
19943     this.wrapEl  = this.el.wrap().wrap();
19944     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19945     
19946     
19947     if(typeof(this.tpl) == "string"){
19948         this.tpl = new Roo.Template(this.tpl);
19949     } else {
19950         // support xtype ctors..
19951         this.tpl = new Roo.factory(this.tpl, Roo);
19952     }
19953     
19954     
19955     this.tpl.compile();
19956     
19957     /** @private */
19958     this.addEvents({
19959         /**
19960          * @event beforeclick
19961          * Fires before a click is processed. Returns false to cancel the default action.
19962          * @param {Roo.View} this
19963          * @param {Number} index The index of the target node
19964          * @param {HTMLElement} node The target node
19965          * @param {Roo.EventObject} e The raw event object
19966          */
19967             "beforeclick" : true,
19968         /**
19969          * @event click
19970          * Fires when a template node is clicked.
19971          * @param {Roo.View} this
19972          * @param {Number} index The index of the target node
19973          * @param {HTMLElement} node The target node
19974          * @param {Roo.EventObject} e The raw event object
19975          */
19976             "click" : true,
19977         /**
19978          * @event dblclick
19979          * Fires when a template node is double clicked.
19980          * @param {Roo.View} this
19981          * @param {Number} index The index of the target node
19982          * @param {HTMLElement} node The target node
19983          * @param {Roo.EventObject} e The raw event object
19984          */
19985             "dblclick" : true,
19986         /**
19987          * @event contextmenu
19988          * Fires when a template node is right clicked.
19989          * @param {Roo.View} this
19990          * @param {Number} index The index of the target node
19991          * @param {HTMLElement} node The target node
19992          * @param {Roo.EventObject} e The raw event object
19993          */
19994             "contextmenu" : true,
19995         /**
19996          * @event selectionchange
19997          * Fires when the selected nodes change.
19998          * @param {Roo.View} this
19999          * @param {Array} selections Array of the selected nodes
20000          */
20001             "selectionchange" : true,
20002     
20003         /**
20004          * @event beforeselect
20005          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20006          * @param {Roo.View} this
20007          * @param {HTMLElement} node The node to be selected
20008          * @param {Array} selections Array of currently selected nodes
20009          */
20010             "beforeselect" : true,
20011         /**
20012          * @event preparedata
20013          * Fires on every row to render, to allow you to change the data.
20014          * @param {Roo.View} this
20015          * @param {Object} data to be rendered (change this)
20016          */
20017           "preparedata" : true
20018           
20019           
20020         });
20021
20022
20023
20024     this.el.on({
20025         "click": this.onClick,
20026         "dblclick": this.onDblClick,
20027         "contextmenu": this.onContextMenu,
20028         scope:this
20029     });
20030
20031     this.selections = [];
20032     this.nodes = [];
20033     this.cmp = new Roo.CompositeElementLite([]);
20034     if(this.store){
20035         this.store = Roo.factory(this.store, Roo.data);
20036         this.setStore(this.store, true);
20037     }
20038     
20039     if ( this.footer && this.footer.xtype) {
20040            
20041          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20042         
20043         this.footer.dataSource = this.store;
20044         this.footer.container = fctr;
20045         this.footer = Roo.factory(this.footer, Roo);
20046         fctr.insertFirst(this.el);
20047         
20048         // this is a bit insane - as the paging toolbar seems to detach the el..
20049 //        dom.parentNode.parentNode.parentNode
20050          // they get detached?
20051     }
20052     
20053     
20054     Roo.View.superclass.constructor.call(this);
20055     
20056     
20057 };
20058
20059 Roo.extend(Roo.View, Roo.util.Observable, {
20060     
20061      /**
20062      * @cfg {Roo.data.Store} store Data store to load data from.
20063      */
20064     store : false,
20065     
20066     /**
20067      * @cfg {String|Roo.Element} el The container element.
20068      */
20069     el : '',
20070     
20071     /**
20072      * @cfg {String|Roo.Template} tpl The template used by this View 
20073      */
20074     tpl : false,
20075     /**
20076      * @cfg {String} dataName the named area of the template to use as the data area
20077      *                          Works with domtemplates roo-name="name"
20078      */
20079     dataName: false,
20080     /**
20081      * @cfg {String} selectedClass The css class to add to selected nodes
20082      */
20083     selectedClass : "x-view-selected",
20084      /**
20085      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20086      */
20087     emptyText : "",
20088     
20089     /**
20090      * @cfg {String} text to display on mask (default Loading)
20091      */
20092     mask : false,
20093     /**
20094      * @cfg {Boolean} multiSelect Allow multiple selection
20095      */
20096     multiSelect : false,
20097     /**
20098      * @cfg {Boolean} singleSelect Allow single selection
20099      */
20100     singleSelect:  false,
20101     
20102     /**
20103      * @cfg {Boolean} toggleSelect - selecting 
20104      */
20105     toggleSelect : false,
20106     
20107     /**
20108      * @cfg {Boolean} tickable - selecting 
20109      */
20110     tickable : false,
20111     
20112     /**
20113      * Returns the element this view is bound to.
20114      * @return {Roo.Element}
20115      */
20116     getEl : function(){
20117         return this.wrapEl;
20118     },
20119     
20120     
20121
20122     /**
20123      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20124      */
20125     refresh : function(){
20126         //Roo.log('refresh');
20127         var t = this.tpl;
20128         
20129         // if we are using something like 'domtemplate', then
20130         // the what gets used is:
20131         // t.applySubtemplate(NAME, data, wrapping data..)
20132         // the outer template then get' applied with
20133         //     the store 'extra data'
20134         // and the body get's added to the
20135         //      roo-name="data" node?
20136         //      <span class='roo-tpl-{name}'></span> ?????
20137         
20138         
20139         
20140         this.clearSelections();
20141         this.el.update("");
20142         var html = [];
20143         var records = this.store.getRange();
20144         if(records.length < 1) {
20145             
20146             // is this valid??  = should it render a template??
20147             
20148             this.el.update(this.emptyText);
20149             return;
20150         }
20151         var el = this.el;
20152         if (this.dataName) {
20153             this.el.update(t.apply(this.store.meta)); //????
20154             el = this.el.child('.roo-tpl-' + this.dataName);
20155         }
20156         
20157         for(var i = 0, len = records.length; i < len; i++){
20158             var data = this.prepareData(records[i].data, i, records[i]);
20159             this.fireEvent("preparedata", this, data, i, records[i]);
20160             
20161             var d = Roo.apply({}, data);
20162             
20163             if(this.tickable){
20164                 Roo.apply(d, {'roo-id' : Roo.id()});
20165                 
20166                 var _this = this;
20167             
20168                 Roo.each(this.parent.item, function(item){
20169                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20170                         return;
20171                     }
20172                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20173                 });
20174             }
20175             
20176             html[html.length] = Roo.util.Format.trim(
20177                 this.dataName ?
20178                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20179                     t.apply(d)
20180             );
20181         }
20182         
20183         
20184         
20185         el.update(html.join(""));
20186         this.nodes = el.dom.childNodes;
20187         this.updateIndexes(0);
20188     },
20189     
20190
20191     /**
20192      * Function to override to reformat the data that is sent to
20193      * the template for each node.
20194      * DEPRICATED - use the preparedata event handler.
20195      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20196      * a JSON object for an UpdateManager bound view).
20197      */
20198     prepareData : function(data, index, record)
20199     {
20200         this.fireEvent("preparedata", this, data, index, record);
20201         return data;
20202     },
20203
20204     onUpdate : function(ds, record){
20205         // Roo.log('on update');   
20206         this.clearSelections();
20207         var index = this.store.indexOf(record);
20208         var n = this.nodes[index];
20209         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20210         n.parentNode.removeChild(n);
20211         this.updateIndexes(index, index);
20212     },
20213
20214     
20215     
20216 // --------- FIXME     
20217     onAdd : function(ds, records, index)
20218     {
20219         //Roo.log(['on Add', ds, records, index] );        
20220         this.clearSelections();
20221         if(this.nodes.length == 0){
20222             this.refresh();
20223             return;
20224         }
20225         var n = this.nodes[index];
20226         for(var i = 0, len = records.length; i < len; i++){
20227             var d = this.prepareData(records[i].data, i, records[i]);
20228             if(n){
20229                 this.tpl.insertBefore(n, d);
20230             }else{
20231                 
20232                 this.tpl.append(this.el, d);
20233             }
20234         }
20235         this.updateIndexes(index);
20236     },
20237
20238     onRemove : function(ds, record, index){
20239        // Roo.log('onRemove');
20240         this.clearSelections();
20241         var el = this.dataName  ?
20242             this.el.child('.roo-tpl-' + this.dataName) :
20243             this.el; 
20244         
20245         el.dom.removeChild(this.nodes[index]);
20246         this.updateIndexes(index);
20247     },
20248
20249     /**
20250      * Refresh an individual node.
20251      * @param {Number} index
20252      */
20253     refreshNode : function(index){
20254         this.onUpdate(this.store, this.store.getAt(index));
20255     },
20256
20257     updateIndexes : function(startIndex, endIndex){
20258         var ns = this.nodes;
20259         startIndex = startIndex || 0;
20260         endIndex = endIndex || ns.length - 1;
20261         for(var i = startIndex; i <= endIndex; i++){
20262             ns[i].nodeIndex = i;
20263         }
20264     },
20265
20266     /**
20267      * Changes the data store this view uses and refresh the view.
20268      * @param {Store} store
20269      */
20270     setStore : function(store, initial){
20271         if(!initial && this.store){
20272             this.store.un("datachanged", this.refresh);
20273             this.store.un("add", this.onAdd);
20274             this.store.un("remove", this.onRemove);
20275             this.store.un("update", this.onUpdate);
20276             this.store.un("clear", this.refresh);
20277             this.store.un("beforeload", this.onBeforeLoad);
20278             this.store.un("load", this.onLoad);
20279             this.store.un("loadexception", this.onLoad);
20280         }
20281         if(store){
20282           
20283             store.on("datachanged", this.refresh, this);
20284             store.on("add", this.onAdd, this);
20285             store.on("remove", this.onRemove, this);
20286             store.on("update", this.onUpdate, this);
20287             store.on("clear", this.refresh, this);
20288             store.on("beforeload", this.onBeforeLoad, this);
20289             store.on("load", this.onLoad, this);
20290             store.on("loadexception", this.onLoad, this);
20291         }
20292         
20293         if(store){
20294             this.refresh();
20295         }
20296     },
20297     /**
20298      * onbeforeLoad - masks the loading area.
20299      *
20300      */
20301     onBeforeLoad : function(store,opts)
20302     {
20303          //Roo.log('onBeforeLoad');   
20304         if (!opts.add) {
20305             this.el.update("");
20306         }
20307         this.el.mask(this.mask ? this.mask : "Loading" ); 
20308     },
20309     onLoad : function ()
20310     {
20311         this.el.unmask();
20312     },
20313     
20314
20315     /**
20316      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20317      * @param {HTMLElement} node
20318      * @return {HTMLElement} The template node
20319      */
20320     findItemFromChild : function(node){
20321         var el = this.dataName  ?
20322             this.el.child('.roo-tpl-' + this.dataName,true) :
20323             this.el.dom; 
20324         
20325         if(!node || node.parentNode == el){
20326                     return node;
20327             }
20328             var p = node.parentNode;
20329             while(p && p != el){
20330             if(p.parentNode == el){
20331                 return p;
20332             }
20333             p = p.parentNode;
20334         }
20335             return null;
20336     },
20337
20338     /** @ignore */
20339     onClick : function(e){
20340         var item = this.findItemFromChild(e.getTarget());
20341         if(item){
20342             var index = this.indexOf(item);
20343             if(this.onItemClick(item, index, e) !== false){
20344                 this.fireEvent("click", this, index, item, e);
20345             }
20346         }else{
20347             this.clearSelections();
20348         }
20349     },
20350
20351     /** @ignore */
20352     onContextMenu : function(e){
20353         var item = this.findItemFromChild(e.getTarget());
20354         if(item){
20355             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20356         }
20357     },
20358
20359     /** @ignore */
20360     onDblClick : function(e){
20361         var item = this.findItemFromChild(e.getTarget());
20362         if(item){
20363             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20364         }
20365     },
20366
20367     onItemClick : function(item, index, e)
20368     {
20369         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20370             return false;
20371         }
20372         if (this.toggleSelect) {
20373             var m = this.isSelected(item) ? 'unselect' : 'select';
20374             //Roo.log(m);
20375             var _t = this;
20376             _t[m](item, true, false);
20377             return true;
20378         }
20379         if(this.multiSelect || this.singleSelect){
20380             if(this.multiSelect && e.shiftKey && this.lastSelection){
20381                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20382             }else{
20383                 this.select(item, this.multiSelect && e.ctrlKey);
20384                 this.lastSelection = item;
20385             }
20386             
20387             if(!this.tickable){
20388                 e.preventDefault();
20389             }
20390             
20391         }
20392         return true;
20393     },
20394
20395     /**
20396      * Get the number of selected nodes.
20397      * @return {Number}
20398      */
20399     getSelectionCount : function(){
20400         return this.selections.length;
20401     },
20402
20403     /**
20404      * Get the currently selected nodes.
20405      * @return {Array} An array of HTMLElements
20406      */
20407     getSelectedNodes : function(){
20408         return this.selections;
20409     },
20410
20411     /**
20412      * Get the indexes of the selected nodes.
20413      * @return {Array}
20414      */
20415     getSelectedIndexes : function(){
20416         var indexes = [], s = this.selections;
20417         for(var i = 0, len = s.length; i < len; i++){
20418             indexes.push(s[i].nodeIndex);
20419         }
20420         return indexes;
20421     },
20422
20423     /**
20424      * Clear all selections
20425      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20426      */
20427     clearSelections : function(suppressEvent){
20428         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20429             this.cmp.elements = this.selections;
20430             this.cmp.removeClass(this.selectedClass);
20431             this.selections = [];
20432             if(!suppressEvent){
20433                 this.fireEvent("selectionchange", this, this.selections);
20434             }
20435         }
20436     },
20437
20438     /**
20439      * Returns true if the passed node is selected
20440      * @param {HTMLElement/Number} node The node or node index
20441      * @return {Boolean}
20442      */
20443     isSelected : function(node){
20444         var s = this.selections;
20445         if(s.length < 1){
20446             return false;
20447         }
20448         node = this.getNode(node);
20449         return s.indexOf(node) !== -1;
20450     },
20451
20452     /**
20453      * Selects nodes.
20454      * @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
20455      * @param {Boolean} keepExisting (optional) true to keep existing selections
20456      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20457      */
20458     select : function(nodeInfo, keepExisting, suppressEvent){
20459         if(nodeInfo instanceof Array){
20460             if(!keepExisting){
20461                 this.clearSelections(true);
20462             }
20463             for(var i = 0, len = nodeInfo.length; i < len; i++){
20464                 this.select(nodeInfo[i], true, true);
20465             }
20466             return;
20467         } 
20468         var node = this.getNode(nodeInfo);
20469         if(!node || this.isSelected(node)){
20470             return; // already selected.
20471         }
20472         if(!keepExisting){
20473             this.clearSelections(true);
20474         }
20475         
20476         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20477             Roo.fly(node).addClass(this.selectedClass);
20478             this.selections.push(node);
20479             if(!suppressEvent){
20480                 this.fireEvent("selectionchange", this, this.selections);
20481             }
20482         }
20483         
20484         
20485     },
20486       /**
20487      * Unselects nodes.
20488      * @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
20489      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20490      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20491      */
20492     unselect : function(nodeInfo, keepExisting, suppressEvent)
20493     {
20494         if(nodeInfo instanceof Array){
20495             Roo.each(this.selections, function(s) {
20496                 this.unselect(s, nodeInfo);
20497             }, this);
20498             return;
20499         }
20500         var node = this.getNode(nodeInfo);
20501         if(!node || !this.isSelected(node)){
20502             //Roo.log("not selected");
20503             return; // not selected.
20504         }
20505         // fireevent???
20506         var ns = [];
20507         Roo.each(this.selections, function(s) {
20508             if (s == node ) {
20509                 Roo.fly(node).removeClass(this.selectedClass);
20510
20511                 return;
20512             }
20513             ns.push(s);
20514         },this);
20515         
20516         this.selections= ns;
20517         this.fireEvent("selectionchange", this, this.selections);
20518     },
20519
20520     /**
20521      * Gets a template node.
20522      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20523      * @return {HTMLElement} The node or null if it wasn't found
20524      */
20525     getNode : function(nodeInfo){
20526         if(typeof nodeInfo == "string"){
20527             return document.getElementById(nodeInfo);
20528         }else if(typeof nodeInfo == "number"){
20529             return this.nodes[nodeInfo];
20530         }
20531         return nodeInfo;
20532     },
20533
20534     /**
20535      * Gets a range template nodes.
20536      * @param {Number} startIndex
20537      * @param {Number} endIndex
20538      * @return {Array} An array of nodes
20539      */
20540     getNodes : function(start, end){
20541         var ns = this.nodes;
20542         start = start || 0;
20543         end = typeof end == "undefined" ? ns.length - 1 : end;
20544         var nodes = [];
20545         if(start <= end){
20546             for(var i = start; i <= end; i++){
20547                 nodes.push(ns[i]);
20548             }
20549         } else{
20550             for(var i = start; i >= end; i--){
20551                 nodes.push(ns[i]);
20552             }
20553         }
20554         return nodes;
20555     },
20556
20557     /**
20558      * Finds the index of the passed node
20559      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20560      * @return {Number} The index of the node or -1
20561      */
20562     indexOf : function(node){
20563         node = this.getNode(node);
20564         if(typeof node.nodeIndex == "number"){
20565             return node.nodeIndex;
20566         }
20567         var ns = this.nodes;
20568         for(var i = 0, len = ns.length; i < len; i++){
20569             if(ns[i] == node){
20570                 return i;
20571             }
20572         }
20573         return -1;
20574     }
20575 });
20576 /*
20577  * - LGPL
20578  *
20579  * based on jquery fullcalendar
20580  * 
20581  */
20582
20583 Roo.bootstrap = Roo.bootstrap || {};
20584 /**
20585  * @class Roo.bootstrap.Calendar
20586  * @extends Roo.bootstrap.Component
20587  * Bootstrap Calendar class
20588  * @cfg {Boolean} loadMask (true|false) default false
20589  * @cfg {Object} header generate the user specific header of the calendar, default false
20590
20591  * @constructor
20592  * Create a new Container
20593  * @param {Object} config The config object
20594  */
20595
20596
20597
20598 Roo.bootstrap.Calendar = function(config){
20599     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20600      this.addEvents({
20601         /**
20602              * @event select
20603              * Fires when a date is selected
20604              * @param {DatePicker} this
20605              * @param {Date} date The selected date
20606              */
20607         'select': true,
20608         /**
20609              * @event monthchange
20610              * Fires when the displayed month changes 
20611              * @param {DatePicker} this
20612              * @param {Date} date The selected month
20613              */
20614         'monthchange': true,
20615         /**
20616              * @event evententer
20617              * Fires when mouse over an event
20618              * @param {Calendar} this
20619              * @param {event} Event
20620              */
20621         'evententer': true,
20622         /**
20623              * @event eventleave
20624              * Fires when the mouse leaves an
20625              * @param {Calendar} this
20626              * @param {event}
20627              */
20628         'eventleave': true,
20629         /**
20630              * @event eventclick
20631              * Fires when the mouse click an
20632              * @param {Calendar} this
20633              * @param {event}
20634              */
20635         'eventclick': true
20636         
20637     });
20638
20639 };
20640
20641 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20642     
20643           /**
20644      * @cfg {Roo.data.Store} store
20645      * The data source for the calendar
20646      */
20647         store : false,
20648      /**
20649      * @cfg {Number} startDay
20650      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20651      */
20652     startDay : 0,
20653     
20654     loadMask : false,
20655     
20656     header : false,
20657       
20658     getAutoCreate : function(){
20659         
20660         
20661         var fc_button = function(name, corner, style, content ) {
20662             return Roo.apply({},{
20663                 tag : 'span',
20664                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20665                          (corner.length ?
20666                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20667                             ''
20668                         ),
20669                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20670                 unselectable: 'on'
20671             });
20672         };
20673         
20674         var header = {};
20675         
20676         if(!this.header){
20677             header = {
20678                 tag : 'table',
20679                 cls : 'fc-header',
20680                 style : 'width:100%',
20681                 cn : [
20682                     {
20683                         tag: 'tr',
20684                         cn : [
20685                             {
20686                                 tag : 'td',
20687                                 cls : 'fc-header-left',
20688                                 cn : [
20689                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20690                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20691                                     { tag: 'span', cls: 'fc-header-space' },
20692                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20693
20694
20695                                 ]
20696                             },
20697
20698                             {
20699                                 tag : 'td',
20700                                 cls : 'fc-header-center',
20701                                 cn : [
20702                                     {
20703                                         tag: 'span',
20704                                         cls: 'fc-header-title',
20705                                         cn : {
20706                                             tag: 'H2',
20707                                             html : 'month / year'
20708                                         }
20709                                     }
20710
20711                                 ]
20712                             },
20713                             {
20714                                 tag : 'td',
20715                                 cls : 'fc-header-right',
20716                                 cn : [
20717                               /*      fc_button('month', 'left', '', 'month' ),
20718                                     fc_button('week', '', '', 'week' ),
20719                                     fc_button('day', 'right', '', 'day' )
20720                                 */    
20721
20722                                 ]
20723                             }
20724
20725                         ]
20726                     }
20727                 ]
20728             };
20729         }
20730         
20731         header = this.header;
20732         
20733        
20734         var cal_heads = function() {
20735             var ret = [];
20736             // fixme - handle this.
20737             
20738             for (var i =0; i < Date.dayNames.length; i++) {
20739                 var d = Date.dayNames[i];
20740                 ret.push({
20741                     tag: 'th',
20742                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20743                     html : d.substring(0,3)
20744                 });
20745                 
20746             }
20747             ret[0].cls += ' fc-first';
20748             ret[6].cls += ' fc-last';
20749             return ret;
20750         };
20751         var cal_cell = function(n) {
20752             return  {
20753                 tag: 'td',
20754                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20755                 cn : [
20756                     {
20757                         cn : [
20758                             {
20759                                 cls: 'fc-day-number',
20760                                 html: 'D'
20761                             },
20762                             {
20763                                 cls: 'fc-day-content',
20764                              
20765                                 cn : [
20766                                      {
20767                                         style: 'position: relative;' // height: 17px;
20768                                     }
20769                                 ]
20770                             }
20771                             
20772                             
20773                         ]
20774                     }
20775                 ]
20776                 
20777             }
20778         };
20779         var cal_rows = function() {
20780             
20781             var ret = [];
20782             for (var r = 0; r < 6; r++) {
20783                 var row= {
20784                     tag : 'tr',
20785                     cls : 'fc-week',
20786                     cn : []
20787                 };
20788                 
20789                 for (var i =0; i < Date.dayNames.length; i++) {
20790                     var d = Date.dayNames[i];
20791                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20792
20793                 }
20794                 row.cn[0].cls+=' fc-first';
20795                 row.cn[0].cn[0].style = 'min-height:90px';
20796                 row.cn[6].cls+=' fc-last';
20797                 ret.push(row);
20798                 
20799             }
20800             ret[0].cls += ' fc-first';
20801             ret[4].cls += ' fc-prev-last';
20802             ret[5].cls += ' fc-last';
20803             return ret;
20804             
20805         };
20806         
20807         var cal_table = {
20808             tag: 'table',
20809             cls: 'fc-border-separate',
20810             style : 'width:100%',
20811             cellspacing  : 0,
20812             cn : [
20813                 { 
20814                     tag: 'thead',
20815                     cn : [
20816                         { 
20817                             tag: 'tr',
20818                             cls : 'fc-first fc-last',
20819                             cn : cal_heads()
20820                         }
20821                     ]
20822                 },
20823                 { 
20824                     tag: 'tbody',
20825                     cn : cal_rows()
20826                 }
20827                   
20828             ]
20829         };
20830          
20831          var cfg = {
20832             cls : 'fc fc-ltr',
20833             cn : [
20834                 header,
20835                 {
20836                     cls : 'fc-content',
20837                     style : "position: relative;",
20838                     cn : [
20839                         {
20840                             cls : 'fc-view fc-view-month fc-grid',
20841                             style : 'position: relative',
20842                             unselectable : 'on',
20843                             cn : [
20844                                 {
20845                                     cls : 'fc-event-container',
20846                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20847                                 },
20848                                 cal_table
20849                             ]
20850                         }
20851                     ]
20852     
20853                 }
20854            ] 
20855             
20856         };
20857         
20858          
20859         
20860         return cfg;
20861     },
20862     
20863     
20864     initEvents : function()
20865     {
20866         if(!this.store){
20867             throw "can not find store for calendar";
20868         }
20869         
20870         var mark = {
20871             tag: "div",
20872             cls:"x-dlg-mask",
20873             style: "text-align:center",
20874             cn: [
20875                 {
20876                     tag: "div",
20877                     style: "background-color:white;width:50%;margin:250 auto",
20878                     cn: [
20879                         {
20880                             tag: "img",
20881                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20882                         },
20883                         {
20884                             tag: "span",
20885                             html: "Loading"
20886                         }
20887                         
20888                     ]
20889                 }
20890             ]
20891         };
20892         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20893         
20894         var size = this.el.select('.fc-content', true).first().getSize();
20895         this.maskEl.setSize(size.width, size.height);
20896         this.maskEl.enableDisplayMode("block");
20897         if(!this.loadMask){
20898             this.maskEl.hide();
20899         }
20900         
20901         this.store = Roo.factory(this.store, Roo.data);
20902         this.store.on('load', this.onLoad, this);
20903         this.store.on('beforeload', this.onBeforeLoad, this);
20904         
20905         this.resize();
20906         
20907         this.cells = this.el.select('.fc-day',true);
20908         //Roo.log(this.cells);
20909         this.textNodes = this.el.query('.fc-day-number');
20910         this.cells.addClassOnOver('fc-state-hover');
20911         
20912         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20913         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20914         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20915         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20916         
20917         this.on('monthchange', this.onMonthChange, this);
20918         
20919         this.update(new Date().clearTime());
20920     },
20921     
20922     resize : function() {
20923         var sz  = this.el.getSize();
20924         
20925         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20926         this.el.select('.fc-day-content div',true).setHeight(34);
20927     },
20928     
20929     
20930     // private
20931     showPrevMonth : function(e){
20932         this.update(this.activeDate.add("mo", -1));
20933     },
20934     showToday : function(e){
20935         this.update(new Date().clearTime());
20936     },
20937     // private
20938     showNextMonth : function(e){
20939         this.update(this.activeDate.add("mo", 1));
20940     },
20941
20942     // private
20943     showPrevYear : function(){
20944         this.update(this.activeDate.add("y", -1));
20945     },
20946
20947     // private
20948     showNextYear : function(){
20949         this.update(this.activeDate.add("y", 1));
20950     },
20951
20952     
20953    // private
20954     update : function(date)
20955     {
20956         var vd = this.activeDate;
20957         this.activeDate = date;
20958 //        if(vd && this.el){
20959 //            var t = date.getTime();
20960 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20961 //                Roo.log('using add remove');
20962 //                
20963 //                this.fireEvent('monthchange', this, date);
20964 //                
20965 //                this.cells.removeClass("fc-state-highlight");
20966 //                this.cells.each(function(c){
20967 //                   if(c.dateValue == t){
20968 //                       c.addClass("fc-state-highlight");
20969 //                       setTimeout(function(){
20970 //                            try{c.dom.firstChild.focus();}catch(e){}
20971 //                       }, 50);
20972 //                       return false;
20973 //                   }
20974 //                   return true;
20975 //                });
20976 //                return;
20977 //            }
20978 //        }
20979         
20980         var days = date.getDaysInMonth();
20981         
20982         var firstOfMonth = date.getFirstDateOfMonth();
20983         var startingPos = firstOfMonth.getDay()-this.startDay;
20984         
20985         if(startingPos < this.startDay){
20986             startingPos += 7;
20987         }
20988         
20989         var pm = date.add(Date.MONTH, -1);
20990         var prevStart = pm.getDaysInMonth()-startingPos;
20991 //        
20992         this.cells = this.el.select('.fc-day',true);
20993         this.textNodes = this.el.query('.fc-day-number');
20994         this.cells.addClassOnOver('fc-state-hover');
20995         
20996         var cells = this.cells.elements;
20997         var textEls = this.textNodes;
20998         
20999         Roo.each(cells, function(cell){
21000             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21001         });
21002         
21003         days += startingPos;
21004
21005         // convert everything to numbers so it's fast
21006         var day = 86400000;
21007         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21008         //Roo.log(d);
21009         //Roo.log(pm);
21010         //Roo.log(prevStart);
21011         
21012         var today = new Date().clearTime().getTime();
21013         var sel = date.clearTime().getTime();
21014         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21015         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21016         var ddMatch = this.disabledDatesRE;
21017         var ddText = this.disabledDatesText;
21018         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21019         var ddaysText = this.disabledDaysText;
21020         var format = this.format;
21021         
21022         var setCellClass = function(cal, cell){
21023             cell.row = 0;
21024             cell.events = [];
21025             cell.more = [];
21026             //Roo.log('set Cell Class');
21027             cell.title = "";
21028             var t = d.getTime();
21029             
21030             //Roo.log(d);
21031             
21032             cell.dateValue = t;
21033             if(t == today){
21034                 cell.className += " fc-today";
21035                 cell.className += " fc-state-highlight";
21036                 cell.title = cal.todayText;
21037             }
21038             if(t == sel){
21039                 // disable highlight in other month..
21040                 //cell.className += " fc-state-highlight";
21041                 
21042             }
21043             // disabling
21044             if(t < min) {
21045                 cell.className = " fc-state-disabled";
21046                 cell.title = cal.minText;
21047                 return;
21048             }
21049             if(t > max) {
21050                 cell.className = " fc-state-disabled";
21051                 cell.title = cal.maxText;
21052                 return;
21053             }
21054             if(ddays){
21055                 if(ddays.indexOf(d.getDay()) != -1){
21056                     cell.title = ddaysText;
21057                     cell.className = " fc-state-disabled";
21058                 }
21059             }
21060             if(ddMatch && format){
21061                 var fvalue = d.dateFormat(format);
21062                 if(ddMatch.test(fvalue)){
21063                     cell.title = ddText.replace("%0", fvalue);
21064                     cell.className = " fc-state-disabled";
21065                 }
21066             }
21067             
21068             if (!cell.initialClassName) {
21069                 cell.initialClassName = cell.dom.className;
21070             }
21071             
21072             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21073         };
21074
21075         var i = 0;
21076         
21077         for(; i < startingPos; i++) {
21078             textEls[i].innerHTML = (++prevStart);
21079             d.setDate(d.getDate()+1);
21080             
21081             cells[i].className = "fc-past fc-other-month";
21082             setCellClass(this, cells[i]);
21083         }
21084         
21085         var intDay = 0;
21086         
21087         for(; i < days; i++){
21088             intDay = i - startingPos + 1;
21089             textEls[i].innerHTML = (intDay);
21090             d.setDate(d.getDate()+1);
21091             
21092             cells[i].className = ''; // "x-date-active";
21093             setCellClass(this, cells[i]);
21094         }
21095         var extraDays = 0;
21096         
21097         for(; i < 42; i++) {
21098             textEls[i].innerHTML = (++extraDays);
21099             d.setDate(d.getDate()+1);
21100             
21101             cells[i].className = "fc-future fc-other-month";
21102             setCellClass(this, cells[i]);
21103         }
21104         
21105         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21106         
21107         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21108         
21109         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21110         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21111         
21112         if(totalRows != 6){
21113             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21114             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21115         }
21116         
21117         this.fireEvent('monthchange', this, date);
21118         
21119         
21120         /*
21121         if(!this.internalRender){
21122             var main = this.el.dom.firstChild;
21123             var w = main.offsetWidth;
21124             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21125             Roo.fly(main).setWidth(w);
21126             this.internalRender = true;
21127             // opera does not respect the auto grow header center column
21128             // then, after it gets a width opera refuses to recalculate
21129             // without a second pass
21130             if(Roo.isOpera && !this.secondPass){
21131                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21132                 this.secondPass = true;
21133                 this.update.defer(10, this, [date]);
21134             }
21135         }
21136         */
21137         
21138     },
21139     
21140     findCell : function(dt) {
21141         dt = dt.clearTime().getTime();
21142         var ret = false;
21143         this.cells.each(function(c){
21144             //Roo.log("check " +c.dateValue + '?=' + dt);
21145             if(c.dateValue == dt){
21146                 ret = c;
21147                 return false;
21148             }
21149             return true;
21150         });
21151         
21152         return ret;
21153     },
21154     
21155     findCells : function(ev) {
21156         var s = ev.start.clone().clearTime().getTime();
21157        // Roo.log(s);
21158         var e= ev.end.clone().clearTime().getTime();
21159        // Roo.log(e);
21160         var ret = [];
21161         this.cells.each(function(c){
21162              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21163             
21164             if(c.dateValue > e){
21165                 return ;
21166             }
21167             if(c.dateValue < s){
21168                 return ;
21169             }
21170             ret.push(c);
21171         });
21172         
21173         return ret;    
21174     },
21175     
21176 //    findBestRow: function(cells)
21177 //    {
21178 //        var ret = 0;
21179 //        
21180 //        for (var i =0 ; i < cells.length;i++) {
21181 //            ret  = Math.max(cells[i].rows || 0,ret);
21182 //        }
21183 //        return ret;
21184 //        
21185 //    },
21186     
21187     
21188     addItem : function(ev)
21189     {
21190         // look for vertical location slot in
21191         var cells = this.findCells(ev);
21192         
21193 //        ev.row = this.findBestRow(cells);
21194         
21195         // work out the location.
21196         
21197         var crow = false;
21198         var rows = [];
21199         for(var i =0; i < cells.length; i++) {
21200             
21201             cells[i].row = cells[0].row;
21202             
21203             if(i == 0){
21204                 cells[i].row = cells[i].row + 1;
21205             }
21206             
21207             if (!crow) {
21208                 crow = {
21209                     start : cells[i],
21210                     end :  cells[i]
21211                 };
21212                 continue;
21213             }
21214             if (crow.start.getY() == cells[i].getY()) {
21215                 // on same row.
21216                 crow.end = cells[i];
21217                 continue;
21218             }
21219             // different row.
21220             rows.push(crow);
21221             crow = {
21222                 start: cells[i],
21223                 end : cells[i]
21224             };
21225             
21226         }
21227         
21228         rows.push(crow);
21229         ev.els = [];
21230         ev.rows = rows;
21231         ev.cells = cells;
21232         
21233         cells[0].events.push(ev);
21234         
21235         this.calevents.push(ev);
21236     },
21237     
21238     clearEvents: function() {
21239         
21240         if(!this.calevents){
21241             return;
21242         }
21243         
21244         Roo.each(this.cells.elements, function(c){
21245             c.row = 0;
21246             c.events = [];
21247             c.more = [];
21248         });
21249         
21250         Roo.each(this.calevents, function(e) {
21251             Roo.each(e.els, function(el) {
21252                 el.un('mouseenter' ,this.onEventEnter, this);
21253                 el.un('mouseleave' ,this.onEventLeave, this);
21254                 el.remove();
21255             },this);
21256         },this);
21257         
21258         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21259             e.remove();
21260         });
21261         
21262     },
21263     
21264     renderEvents: function()
21265     {   
21266         var _this = this;
21267         
21268         this.cells.each(function(c) {
21269             
21270             if(c.row < 5){
21271                 return;
21272             }
21273             
21274             var ev = c.events;
21275             
21276             var r = 4;
21277             if(c.row != c.events.length){
21278                 r = 4 - (4 - (c.row - c.events.length));
21279             }
21280             
21281             c.events = ev.slice(0, r);
21282             c.more = ev.slice(r);
21283             
21284             if(c.more.length && c.more.length == 1){
21285                 c.events.push(c.more.pop());
21286             }
21287             
21288             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21289             
21290         });
21291             
21292         this.cells.each(function(c) {
21293             
21294             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21295             
21296             
21297             for (var e = 0; e < c.events.length; e++){
21298                 var ev = c.events[e];
21299                 var rows = ev.rows;
21300                 
21301                 for(var i = 0; i < rows.length; i++) {
21302                 
21303                     // how many rows should it span..
21304
21305                     var  cfg = {
21306                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21307                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21308
21309                         unselectable : "on",
21310                         cn : [
21311                             {
21312                                 cls: 'fc-event-inner',
21313                                 cn : [
21314     //                                {
21315     //                                  tag:'span',
21316     //                                  cls: 'fc-event-time',
21317     //                                  html : cells.length > 1 ? '' : ev.time
21318     //                                },
21319                                     {
21320                                       tag:'span',
21321                                       cls: 'fc-event-title',
21322                                       html : String.format('{0}', ev.title)
21323                                     }
21324
21325
21326                                 ]
21327                             },
21328                             {
21329                                 cls: 'ui-resizable-handle ui-resizable-e',
21330                                 html : '&nbsp;&nbsp;&nbsp'
21331                             }
21332
21333                         ]
21334                     };
21335
21336                     if (i == 0) {
21337                         cfg.cls += ' fc-event-start';
21338                     }
21339                     if ((i+1) == rows.length) {
21340                         cfg.cls += ' fc-event-end';
21341                     }
21342
21343                     var ctr = _this.el.select('.fc-event-container',true).first();
21344                     var cg = ctr.createChild(cfg);
21345
21346                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21347                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21348
21349                     var r = (c.more.length) ? 1 : 0;
21350                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21351                     cg.setWidth(ebox.right - sbox.x -2);
21352
21353                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21354                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21355                     cg.on('click', _this.onEventClick, _this, ev);
21356
21357                     ev.els.push(cg);
21358                     
21359                 }
21360                 
21361             }
21362             
21363             
21364             if(c.more.length){
21365                 var  cfg = {
21366                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21367                     style : 'position: absolute',
21368                     unselectable : "on",
21369                     cn : [
21370                         {
21371                             cls: 'fc-event-inner',
21372                             cn : [
21373                                 {
21374                                   tag:'span',
21375                                   cls: 'fc-event-title',
21376                                   html : 'More'
21377                                 }
21378
21379
21380                             ]
21381                         },
21382                         {
21383                             cls: 'ui-resizable-handle ui-resizable-e',
21384                             html : '&nbsp;&nbsp;&nbsp'
21385                         }
21386
21387                     ]
21388                 };
21389
21390                 var ctr = _this.el.select('.fc-event-container',true).first();
21391                 var cg = ctr.createChild(cfg);
21392
21393                 var sbox = c.select('.fc-day-content',true).first().getBox();
21394                 var ebox = c.select('.fc-day-content',true).first().getBox();
21395                 //Roo.log(cg);
21396                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21397                 cg.setWidth(ebox.right - sbox.x -2);
21398
21399                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21400                 
21401             }
21402             
21403         });
21404         
21405         
21406         
21407     },
21408     
21409     onEventEnter: function (e, el,event,d) {
21410         this.fireEvent('evententer', this, el, event);
21411     },
21412     
21413     onEventLeave: function (e, el,event,d) {
21414         this.fireEvent('eventleave', this, el, event);
21415     },
21416     
21417     onEventClick: function (e, el,event,d) {
21418         this.fireEvent('eventclick', this, el, event);
21419     },
21420     
21421     onMonthChange: function () {
21422         this.store.load();
21423     },
21424     
21425     onMoreEventClick: function(e, el, more)
21426     {
21427         var _this = this;
21428         
21429         this.calpopover.placement = 'right';
21430         this.calpopover.setTitle('More');
21431         
21432         this.calpopover.setContent('');
21433         
21434         var ctr = this.calpopover.el.select('.popover-content', true).first();
21435         
21436         Roo.each(more, function(m){
21437             var cfg = {
21438                 cls : 'fc-event-hori fc-event-draggable',
21439                 html : m.title
21440             };
21441             var cg = ctr.createChild(cfg);
21442             
21443             cg.on('click', _this.onEventClick, _this, m);
21444         });
21445         
21446         this.calpopover.show(el);
21447         
21448         
21449     },
21450     
21451     onLoad: function () 
21452     {   
21453         this.calevents = [];
21454         var cal = this;
21455         
21456         if(this.store.getCount() > 0){
21457             this.store.data.each(function(d){
21458                cal.addItem({
21459                     id : d.data.id,
21460                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21461                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21462                     time : d.data.start_time,
21463                     title : d.data.title,
21464                     description : d.data.description,
21465                     venue : d.data.venue
21466                 });
21467             });
21468         }
21469         
21470         this.renderEvents();
21471         
21472         if(this.calevents.length && this.loadMask){
21473             this.maskEl.hide();
21474         }
21475     },
21476     
21477     onBeforeLoad: function()
21478     {
21479         this.clearEvents();
21480         if(this.loadMask){
21481             this.maskEl.show();
21482         }
21483     }
21484 });
21485
21486  
21487  /*
21488  * - LGPL
21489  *
21490  * element
21491  * 
21492  */
21493
21494 /**
21495  * @class Roo.bootstrap.Popover
21496  * @extends Roo.bootstrap.Component
21497  * @parent none builder
21498  * @children Roo.bootstrap.Component
21499  * Bootstrap Popover class
21500  * @cfg {String} html contents of the popover   (or false to use children..)
21501  * @cfg {String} title of popover (or false to hide)
21502  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21503  * @cfg {String} trigger click || hover (or false to trigger manually)
21504  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21505  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21506  *      - if false and it has a 'parent' then it will be automatically added to that element
21507  *      - if string - Roo.get  will be called 
21508  * @cfg {Number} delay - delay before showing
21509  
21510  * @constructor
21511  * Create a new Popover
21512  * @param {Object} config The config object
21513  */
21514
21515 Roo.bootstrap.Popover = function(config){
21516     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21517     
21518     this.addEvents({
21519         // raw events
21520          /**
21521          * @event show
21522          * After the popover show
21523          * 
21524          * @param {Roo.bootstrap.Popover} this
21525          */
21526         "show" : true,
21527         /**
21528          * @event hide
21529          * After the popover hide
21530          * 
21531          * @param {Roo.bootstrap.Popover} this
21532          */
21533         "hide" : true
21534     });
21535 };
21536
21537 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21538     
21539     title: false,
21540     html: false,
21541     
21542     placement : 'right',
21543     trigger : 'hover', // hover
21544     modal : false,
21545     delay : 0,
21546     
21547     over: false,
21548     
21549     can_build_overlaid : false,
21550     
21551     maskEl : false, // the mask element
21552     headerEl : false,
21553     contentEl : false,
21554     alignEl : false, // when show is called with an element - this get's stored.
21555     
21556     getChildContainer : function()
21557     {
21558         return this.contentEl;
21559         
21560     },
21561     getPopoverHeader : function()
21562     {
21563         this.title = true; // flag not to hide it..
21564         this.headerEl.addClass('p-0');
21565         return this.headerEl
21566     },
21567     
21568     
21569     getAutoCreate : function(){
21570          
21571         var cfg = {
21572            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21573            style: 'display:block',
21574            cn : [
21575                 {
21576                     cls : 'arrow'
21577                 },
21578                 {
21579                     cls : 'popover-inner ',
21580                     cn : [
21581                         {
21582                             tag: 'h3',
21583                             cls: 'popover-title popover-header',
21584                             html : this.title === false ? '' : this.title
21585                         },
21586                         {
21587                             cls : 'popover-content popover-body '  + (this.cls || ''),
21588                             html : this.html || ''
21589                         }
21590                     ]
21591                     
21592                 }
21593            ]
21594         };
21595         
21596         return cfg;
21597     },
21598     /**
21599      * @param {string} the title
21600      */
21601     setTitle: function(str)
21602     {
21603         this.title = str;
21604         if (this.el) {
21605             this.headerEl.dom.innerHTML = str;
21606         }
21607         
21608     },
21609     /**
21610      * @param {string} the body content
21611      */
21612     setContent: function(str)
21613     {
21614         this.html = str;
21615         if (this.contentEl) {
21616             this.contentEl.dom.innerHTML = str;
21617         }
21618         
21619     },
21620     // as it get's added to the bottom of the page.
21621     onRender : function(ct, position)
21622     {
21623         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21624         
21625         
21626         
21627         if(!this.el){
21628             var cfg = Roo.apply({},  this.getAutoCreate());
21629             cfg.id = Roo.id();
21630             
21631             if (this.cls) {
21632                 cfg.cls += ' ' + this.cls;
21633             }
21634             if (this.style) {
21635                 cfg.style = this.style;
21636             }
21637             //Roo.log("adding to ");
21638             this.el = Roo.get(document.body).createChild(cfg, position);
21639 //            Roo.log(this.el);
21640         }
21641         
21642         this.contentEl = this.el.select('.popover-content',true).first();
21643         this.headerEl =  this.el.select('.popover-title',true).first();
21644         
21645         var nitems = [];
21646         if(typeof(this.items) != 'undefined'){
21647             var items = this.items;
21648             delete this.items;
21649
21650             for(var i =0;i < items.length;i++) {
21651                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21652             }
21653         }
21654
21655         this.items = nitems;
21656         
21657         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21658         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21659         
21660         
21661         
21662         this.initEvents();
21663     },
21664     
21665     resizeMask : function()
21666     {
21667         this.maskEl.setSize(
21668             Roo.lib.Dom.getViewWidth(true),
21669             Roo.lib.Dom.getViewHeight(true)
21670         );
21671     },
21672     
21673     initEvents : function()
21674     {
21675         
21676         if (!this.modal) { 
21677             Roo.bootstrap.Popover.register(this);
21678         }
21679          
21680         this.arrowEl = this.el.select('.arrow',true).first();
21681         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21682         this.el.enableDisplayMode('block');
21683         this.el.hide();
21684  
21685         
21686         if (this.over === false && !this.parent()) {
21687             return; 
21688         }
21689         if (this.triggers === false) {
21690             return;
21691         }
21692          
21693         // support parent
21694         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21695         var triggers = this.trigger ? this.trigger.split(' ') : [];
21696         Roo.each(triggers, function(trigger) {
21697         
21698             if (trigger == 'click') {
21699                 on_el.on('click', this.toggle, this);
21700             } else if (trigger != 'manual') {
21701                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21702                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21703       
21704                 on_el.on(eventIn  ,this.enter, this);
21705                 on_el.on(eventOut, this.leave, this);
21706             }
21707         }, this);
21708     },
21709     
21710     
21711     // private
21712     timeout : null,
21713     hoverState : null,
21714     
21715     toggle : function () {
21716         this.hoverState == 'in' ? this.leave() : this.enter();
21717     },
21718     
21719     enter : function () {
21720         
21721         clearTimeout(this.timeout);
21722     
21723         this.hoverState = 'in';
21724     
21725         if (!this.delay || !this.delay.show) {
21726             this.show();
21727             return;
21728         }
21729         var _t = this;
21730         this.timeout = setTimeout(function () {
21731             if (_t.hoverState == 'in') {
21732                 _t.show();
21733             }
21734         }, this.delay.show)
21735     },
21736     
21737     leave : function() {
21738         clearTimeout(this.timeout);
21739     
21740         this.hoverState = 'out';
21741     
21742         if (!this.delay || !this.delay.hide) {
21743             this.hide();
21744             return;
21745         }
21746         var _t = this;
21747         this.timeout = setTimeout(function () {
21748             if (_t.hoverState == 'out') {
21749                 _t.hide();
21750             }
21751         }, this.delay.hide)
21752     },
21753     
21754     /**
21755      * update the position of the dialog
21756      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21757      * 
21758      *
21759      */
21760     
21761     doAlign : function()
21762     {
21763         
21764         if (this.alignEl) {
21765             this.updatePosition(this.placement, true);
21766              
21767         } else {
21768             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21769             var es = this.el.getSize();
21770             var x = Roo.lib.Dom.getViewWidth()/2;
21771             var y = Roo.lib.Dom.getViewHeight()/2;
21772             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21773             
21774         }
21775
21776          
21777          
21778         
21779         
21780     },
21781     
21782     /**
21783      * Show the popover
21784      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21785      * @param {string} (left|right|top|bottom) position
21786      */
21787     show : function (on_el, placement)
21788     {
21789         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21790         on_el = on_el || false; // default to false
21791          
21792         if (!on_el) {
21793             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21794                 on_el = this.parent().el;
21795             } else if (this.over) {
21796                 on_el = Roo.get(this.over);
21797             }
21798             
21799         }
21800         
21801         this.alignEl = Roo.get( on_el );
21802
21803         if (!this.el) {
21804             this.render(document.body);
21805         }
21806         
21807         
21808          
21809         
21810         if (this.title === false) {
21811             this.headerEl.hide();
21812         }
21813         
21814        
21815         this.el.show();
21816         this.el.dom.style.display = 'block';
21817          
21818         this.doAlign();
21819         
21820         //var arrow = this.el.select('.arrow',true).first();
21821         //arrow.set(align[2], 
21822         
21823         this.el.addClass('in');
21824         
21825          
21826         
21827         this.hoverState = 'in';
21828         
21829         if (this.modal) {
21830             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21831             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21832             this.maskEl.dom.style.display = 'block';
21833             this.maskEl.addClass('show');
21834         }
21835         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21836  
21837         this.fireEvent('show', this);
21838         
21839     },
21840     /**
21841      * fire this manually after loading a grid in the table for example
21842      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21843      * @param {Boolean} try and move it if we cant get right position.
21844      */
21845     updatePosition : function(placement, try_move)
21846     {
21847         // allow for calling with no parameters
21848         placement = placement   ? placement :  this.placement;
21849         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21850         
21851         this.el.removeClass([
21852             'fade','top','bottom', 'left', 'right','in',
21853             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21854         ]);
21855         this.el.addClass(placement + ' bs-popover-' + placement);
21856         
21857         if (!this.alignEl ) {
21858             return false;
21859         }
21860         
21861         switch (placement) {
21862             case 'right':
21863                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21864                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21865                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21866                     //normal display... or moved up/down.
21867                     this.el.setXY(offset);
21868                     var xy = this.alignEl.getAnchorXY('tr', false);
21869                     xy[0]+=2;xy[1]+=5;
21870                     this.arrowEl.setXY(xy);
21871                     return true;
21872                 }
21873                 // continue through...
21874                 return this.updatePosition('left', false);
21875                 
21876             
21877             case 'left':
21878                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21879                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21880                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21881                     //normal display... or moved up/down.
21882                     this.el.setXY(offset);
21883                     var xy = this.alignEl.getAnchorXY('tl', false);
21884                     xy[0]-=10;xy[1]+=5; // << fix me
21885                     this.arrowEl.setXY(xy);
21886                     return true;
21887                 }
21888                 // call self...
21889                 return this.updatePosition('right', false);
21890             
21891             case 'top':
21892                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21893                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21894                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21895                     //normal display... or moved up/down.
21896                     this.el.setXY(offset);
21897                     var xy = this.alignEl.getAnchorXY('t', false);
21898                     xy[1]-=10; // << fix me
21899                     this.arrowEl.setXY(xy);
21900                     return true;
21901                 }
21902                 // fall through
21903                return this.updatePosition('bottom', false);
21904             
21905             case 'bottom':
21906                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21907                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21908                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21909                     //normal display... or moved up/down.
21910                     this.el.setXY(offset);
21911                     var xy = this.alignEl.getAnchorXY('b', false);
21912                      xy[1]+=2; // << fix me
21913                     this.arrowEl.setXY(xy);
21914                     return true;
21915                 }
21916                 // fall through
21917                 return this.updatePosition('top', false);
21918                 
21919             
21920         }
21921         
21922         
21923         return false;
21924     },
21925     
21926     hide : function()
21927     {
21928         this.el.setXY([0,0]);
21929         this.el.removeClass('in');
21930         this.el.hide();
21931         this.hoverState = null;
21932         this.maskEl.hide(); // always..
21933         this.fireEvent('hide', this);
21934     }
21935     
21936 });
21937
21938
21939 Roo.apply(Roo.bootstrap.Popover, {
21940
21941     alignment : {
21942         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21943         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21944         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21945         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21946     },
21947     
21948     zIndex : 20001,
21949
21950     clickHander : false,
21951     
21952     
21953
21954     onMouseDown : function(e)
21955     {
21956         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21957             /// what is nothing is showing..
21958             this.hideAll();
21959         }
21960          
21961     },
21962     
21963     
21964     popups : [],
21965     
21966     register : function(popup)
21967     {
21968         if (!Roo.bootstrap.Popover.clickHandler) {
21969             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21970         }
21971         // hide other popups.
21972         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21973         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21974         this.hideAll(); //<< why?
21975         //this.popups.push(popup);
21976     },
21977     hideAll : function()
21978     {
21979         this.popups.forEach(function(p) {
21980             p.hide();
21981         });
21982     },
21983     onShow : function() {
21984         Roo.bootstrap.Popover.popups.push(this);
21985     },
21986     onHide : function() {
21987         Roo.bootstrap.Popover.popups.remove(this);
21988     } 
21989
21990 });
21991 /**
21992  * @class Roo.bootstrap.PopoverNav
21993  * @extends Roo.bootstrap.nav.Simplebar
21994  * @parent Roo.bootstrap.Popover
21995  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21996  * @licence LGPL
21997  * Bootstrap Popover header navigation class
21998  * FIXME? should this go under nav?
21999  *
22000  * 
22001  * @constructor
22002  * Create a new Popover Header Navigation 
22003  * @param {Object} config The config object
22004  */
22005
22006 Roo.bootstrap.PopoverNav = function(config){
22007     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22008 };
22009
22010 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22011     
22012     
22013     container_method : 'getPopoverHeader' 
22014     
22015      
22016     
22017     
22018    
22019 });
22020
22021  
22022
22023  /*
22024  * - LGPL
22025  *
22026  * Progress
22027  * 
22028  */
22029
22030 /**
22031  * @class Roo.bootstrap.Progress
22032  * @extends Roo.bootstrap.Component
22033  * @children Roo.bootstrap.ProgressBar
22034  * Bootstrap Progress class
22035  * @cfg {Boolean} striped striped of the progress bar
22036  * @cfg {Boolean} active animated of the progress bar
22037  * 
22038  * 
22039  * @constructor
22040  * Create a new Progress
22041  * @param {Object} config The config object
22042  */
22043
22044 Roo.bootstrap.Progress = function(config){
22045     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22046 };
22047
22048 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22049     
22050     striped : false,
22051     active: false,
22052     
22053     getAutoCreate : function(){
22054         var cfg = {
22055             tag: 'div',
22056             cls: 'progress'
22057         };
22058         
22059         
22060         if(this.striped){
22061             cfg.cls += ' progress-striped';
22062         }
22063       
22064         if(this.active){
22065             cfg.cls += ' active';
22066         }
22067         
22068         
22069         return cfg;
22070     }
22071    
22072 });
22073
22074  
22075
22076  /*
22077  * - LGPL
22078  *
22079  * ProgressBar
22080  * 
22081  */
22082
22083 /**
22084  * @class Roo.bootstrap.ProgressBar
22085  * @extends Roo.bootstrap.Component
22086  * Bootstrap ProgressBar class
22087  * @cfg {Number} aria_valuenow aria-value now
22088  * @cfg {Number} aria_valuemin aria-value min
22089  * @cfg {Number} aria_valuemax aria-value max
22090  * @cfg {String} label label for the progress bar
22091  * @cfg {String} panel (success | info | warning | danger )
22092  * @cfg {String} role role of the progress bar
22093  * @cfg {String} sr_only text
22094  * 
22095  * 
22096  * @constructor
22097  * Create a new ProgressBar
22098  * @param {Object} config The config object
22099  */
22100
22101 Roo.bootstrap.ProgressBar = function(config){
22102     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22103 };
22104
22105 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22106     
22107     aria_valuenow : 0,
22108     aria_valuemin : 0,
22109     aria_valuemax : 100,
22110     label : false,
22111     panel : false,
22112     role : false,
22113     sr_only: false,
22114     
22115     getAutoCreate : function()
22116     {
22117         
22118         var cfg = {
22119             tag: 'div',
22120             cls: 'progress-bar',
22121             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22122         };
22123         
22124         if(this.sr_only){
22125             cfg.cn = {
22126                 tag: 'span',
22127                 cls: 'sr-only',
22128                 html: this.sr_only
22129             }
22130         }
22131         
22132         if(this.role){
22133             cfg.role = this.role;
22134         }
22135         
22136         if(this.aria_valuenow){
22137             cfg['aria-valuenow'] = this.aria_valuenow;
22138         }
22139         
22140         if(this.aria_valuemin){
22141             cfg['aria-valuemin'] = this.aria_valuemin;
22142         }
22143         
22144         if(this.aria_valuemax){
22145             cfg['aria-valuemax'] = this.aria_valuemax;
22146         }
22147         
22148         if(this.label && !this.sr_only){
22149             cfg.html = this.label;
22150         }
22151         
22152         if(this.panel){
22153             cfg.cls += ' progress-bar-' + this.panel;
22154         }
22155         
22156         return cfg;
22157     },
22158     
22159     update : function(aria_valuenow)
22160     {
22161         this.aria_valuenow = aria_valuenow;
22162         
22163         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22164     }
22165    
22166 });
22167
22168  
22169
22170  /**
22171  * @class Roo.bootstrap.TabGroup
22172  * @extends Roo.bootstrap.Column
22173  * @children Roo.bootstrap.TabPanel
22174  * Bootstrap Column class
22175  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22176  * @cfg {Boolean} carousel true to make the group behave like a carousel
22177  * @cfg {Boolean} bullets show bullets for the panels
22178  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22179  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22180  * @cfg {Boolean} showarrow (true|false) show arrow default true
22181  * 
22182  * @constructor
22183  * Create a new TabGroup
22184  * @param {Object} config The config object
22185  */
22186
22187 Roo.bootstrap.TabGroup = function(config){
22188     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22189     if (!this.navId) {
22190         this.navId = Roo.id();
22191     }
22192     this.tabs = [];
22193     Roo.bootstrap.TabGroup.register(this);
22194     
22195 };
22196
22197 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22198     
22199     carousel : false,
22200     transition : false,
22201     bullets : 0,
22202     timer : 0,
22203     autoslide : false,
22204     slideFn : false,
22205     slideOnTouch : false,
22206     showarrow : true,
22207     
22208     getAutoCreate : function()
22209     {
22210         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22211         
22212         cfg.cls += ' tab-content';
22213         
22214         if (this.carousel) {
22215             cfg.cls += ' carousel slide';
22216             
22217             cfg.cn = [{
22218                cls : 'carousel-inner',
22219                cn : []
22220             }];
22221         
22222             if(this.bullets  && !Roo.isTouch){
22223                 
22224                 var bullets = {
22225                     cls : 'carousel-bullets',
22226                     cn : []
22227                 };
22228                
22229                 if(this.bullets_cls){
22230                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22231                 }
22232                 
22233                 bullets.cn.push({
22234                     cls : 'clear'
22235                 });
22236                 
22237                 cfg.cn[0].cn.push(bullets);
22238             }
22239             
22240             if(this.showarrow){
22241                 cfg.cn[0].cn.push({
22242                     tag : 'div',
22243                     class : 'carousel-arrow',
22244                     cn : [
22245                         {
22246                             tag : 'div',
22247                             class : 'carousel-prev',
22248                             cn : [
22249                                 {
22250                                     tag : 'i',
22251                                     class : 'fa fa-chevron-left'
22252                                 }
22253                             ]
22254                         },
22255                         {
22256                             tag : 'div',
22257                             class : 'carousel-next',
22258                             cn : [
22259                                 {
22260                                     tag : 'i',
22261                                     class : 'fa fa-chevron-right'
22262                                 }
22263                             ]
22264                         }
22265                     ]
22266                 });
22267             }
22268             
22269         }
22270         
22271         return cfg;
22272     },
22273     
22274     initEvents:  function()
22275     {
22276 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22277 //            this.el.on("touchstart", this.onTouchStart, this);
22278 //        }
22279         
22280         if(this.autoslide){
22281             var _this = this;
22282             
22283             this.slideFn = window.setInterval(function() {
22284                 _this.showPanelNext();
22285             }, this.timer);
22286         }
22287         
22288         if(this.showarrow){
22289             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22290             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22291         }
22292         
22293         
22294     },
22295     
22296 //    onTouchStart : function(e, el, o)
22297 //    {
22298 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22299 //            return;
22300 //        }
22301 //        
22302 //        this.showPanelNext();
22303 //    },
22304     
22305     
22306     getChildContainer : function()
22307     {
22308         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22309     },
22310     
22311     /**
22312     * register a Navigation item
22313     * @param {Roo.bootstrap.nav.Item} the navitem to add
22314     */
22315     register : function(item)
22316     {
22317         this.tabs.push( item);
22318         item.navId = this.navId; // not really needed..
22319         this.addBullet();
22320     
22321     },
22322     
22323     getActivePanel : function()
22324     {
22325         var r = false;
22326         Roo.each(this.tabs, function(t) {
22327             if (t.active) {
22328                 r = t;
22329                 return false;
22330             }
22331             return null;
22332         });
22333         return r;
22334         
22335     },
22336     getPanelByName : function(n)
22337     {
22338         var r = false;
22339         Roo.each(this.tabs, function(t) {
22340             if (t.tabId == n) {
22341                 r = t;
22342                 return false;
22343             }
22344             return null;
22345         });
22346         return r;
22347     },
22348     indexOfPanel : function(p)
22349     {
22350         var r = false;
22351         Roo.each(this.tabs, function(t,i) {
22352             if (t.tabId == p.tabId) {
22353                 r = i;
22354                 return false;
22355             }
22356             return null;
22357         });
22358         return r;
22359     },
22360     /**
22361      * show a specific panel
22362      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22363      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22364      */
22365     showPanel : function (pan)
22366     {
22367         if(this.transition || typeof(pan) == 'undefined'){
22368             Roo.log("waiting for the transitionend");
22369             return false;
22370         }
22371         
22372         if (typeof(pan) == 'number') {
22373             pan = this.tabs[pan];
22374         }
22375         
22376         if (typeof(pan) == 'string') {
22377             pan = this.getPanelByName(pan);
22378         }
22379         
22380         var cur = this.getActivePanel();
22381         
22382         if(!pan || !cur){
22383             Roo.log('pan or acitve pan is undefined');
22384             return false;
22385         }
22386         
22387         if (pan.tabId == this.getActivePanel().tabId) {
22388             return true;
22389         }
22390         
22391         if (false === cur.fireEvent('beforedeactivate')) {
22392             return false;
22393         }
22394         
22395         if(this.bullets > 0 && !Roo.isTouch){
22396             this.setActiveBullet(this.indexOfPanel(pan));
22397         }
22398         
22399         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22400             
22401             //class="carousel-item carousel-item-next carousel-item-left"
22402             
22403             this.transition = true;
22404             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22405             var lr = dir == 'next' ? 'left' : 'right';
22406             pan.el.addClass(dir); // or prev
22407             pan.el.addClass('carousel-item-' + dir); // or prev
22408             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22409             cur.el.addClass(lr); // or right
22410             pan.el.addClass(lr);
22411             cur.el.addClass('carousel-item-' +lr); // or right
22412             pan.el.addClass('carousel-item-' +lr);
22413             
22414             
22415             var _this = this;
22416             cur.el.on('transitionend', function() {
22417                 Roo.log("trans end?");
22418                 
22419                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22420                 pan.setActive(true);
22421                 
22422                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22423                 cur.setActive(false);
22424                 
22425                 _this.transition = false;
22426                 
22427             }, this, { single:  true } );
22428             
22429             return true;
22430         }
22431         
22432         cur.setActive(false);
22433         pan.setActive(true);
22434         
22435         return true;
22436         
22437     },
22438     showPanelNext : function()
22439     {
22440         var i = this.indexOfPanel(this.getActivePanel());
22441         
22442         if (i >= this.tabs.length - 1 && !this.autoslide) {
22443             return;
22444         }
22445         
22446         if (i >= this.tabs.length - 1 && this.autoslide) {
22447             i = -1;
22448         }
22449         
22450         this.showPanel(this.tabs[i+1]);
22451     },
22452     
22453     showPanelPrev : function()
22454     {
22455         var i = this.indexOfPanel(this.getActivePanel());
22456         
22457         if (i  < 1 && !this.autoslide) {
22458             return;
22459         }
22460         
22461         if (i < 1 && this.autoslide) {
22462             i = this.tabs.length;
22463         }
22464         
22465         this.showPanel(this.tabs[i-1]);
22466     },
22467     
22468     
22469     addBullet: function()
22470     {
22471         if(!this.bullets || Roo.isTouch){
22472             return;
22473         }
22474         var ctr = this.el.select('.carousel-bullets',true).first();
22475         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22476         var bullet = ctr.createChild({
22477             cls : 'bullet bullet-' + i
22478         },ctr.dom.lastChild);
22479         
22480         
22481         var _this = this;
22482         
22483         bullet.on('click', (function(e, el, o, ii, t){
22484
22485             e.preventDefault();
22486
22487             this.showPanel(ii);
22488
22489             if(this.autoslide && this.slideFn){
22490                 clearInterval(this.slideFn);
22491                 this.slideFn = window.setInterval(function() {
22492                     _this.showPanelNext();
22493                 }, this.timer);
22494             }
22495
22496         }).createDelegate(this, [i, bullet], true));
22497                 
22498         
22499     },
22500      
22501     setActiveBullet : function(i)
22502     {
22503         if(Roo.isTouch){
22504             return;
22505         }
22506         
22507         Roo.each(this.el.select('.bullet', true).elements, function(el){
22508             el.removeClass('selected');
22509         });
22510
22511         var bullet = this.el.select('.bullet-' + i, true).first();
22512         
22513         if(!bullet){
22514             return;
22515         }
22516         
22517         bullet.addClass('selected');
22518     }
22519     
22520     
22521   
22522 });
22523
22524  
22525
22526  
22527  
22528 Roo.apply(Roo.bootstrap.TabGroup, {
22529     
22530     groups: {},
22531      /**
22532     * register a Navigation Group
22533     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22534     */
22535     register : function(navgrp)
22536     {
22537         this.groups[navgrp.navId] = navgrp;
22538         
22539     },
22540     /**
22541     * fetch a Navigation Group based on the navigation ID
22542     * if one does not exist , it will get created.
22543     * @param {string} the navgroup to add
22544     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22545     */
22546     get: function(navId) {
22547         if (typeof(this.groups[navId]) == 'undefined') {
22548             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22549         }
22550         return this.groups[navId] ;
22551     }
22552     
22553     
22554     
22555 });
22556
22557  /*
22558  * - LGPL
22559  *
22560  * TabPanel
22561  * 
22562  */
22563
22564 /**
22565  * @class Roo.bootstrap.TabPanel
22566  * @extends Roo.bootstrap.Component
22567  * @children Roo.bootstrap.Component
22568  * Bootstrap TabPanel class
22569  * @cfg {Boolean} active panel active
22570  * @cfg {String} html panel content
22571  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22572  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22573  * @cfg {String} href click to link..
22574  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22575  * 
22576  * 
22577  * @constructor
22578  * Create a new TabPanel
22579  * @param {Object} config The config object
22580  */
22581
22582 Roo.bootstrap.TabPanel = function(config){
22583     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22584     this.addEvents({
22585         /**
22586              * @event changed
22587              * Fires when the active status changes
22588              * @param {Roo.bootstrap.TabPanel} this
22589              * @param {Boolean} state the new state
22590             
22591          */
22592         'changed': true,
22593         /**
22594              * @event beforedeactivate
22595              * Fires before a tab is de-activated - can be used to do validation on a form.
22596              * @param {Roo.bootstrap.TabPanel} this
22597              * @return {Boolean} false if there is an error
22598             
22599          */
22600         'beforedeactivate': true
22601      });
22602     
22603     this.tabId = this.tabId || Roo.id();
22604   
22605 };
22606
22607 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22608     
22609     active: false,
22610     html: false,
22611     tabId: false,
22612     navId : false,
22613     href : '',
22614     touchSlide : false,
22615     getAutoCreate : function(){
22616         
22617         
22618         var cfg = {
22619             tag: 'div',
22620             // item is needed for carousel - not sure if it has any effect otherwise
22621             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22622             html: this.html || ''
22623         };
22624         
22625         if(this.active){
22626             cfg.cls += ' active';
22627         }
22628         
22629         if(this.tabId){
22630             cfg.tabId = this.tabId;
22631         }
22632         
22633         
22634         
22635         return cfg;
22636     },
22637     
22638     initEvents:  function()
22639     {
22640         var p = this.parent();
22641         
22642         this.navId = this.navId || p.navId;
22643         
22644         if (typeof(this.navId) != 'undefined') {
22645             // not really needed.. but just in case.. parent should be a NavGroup.
22646             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22647             
22648             tg.register(this);
22649             
22650             var i = tg.tabs.length - 1;
22651             
22652             if(this.active && tg.bullets > 0 && i < tg.bullets){
22653                 tg.setActiveBullet(i);
22654             }
22655         }
22656         
22657         this.el.on('click', this.onClick, this);
22658         
22659         if(Roo.isTouch && this.touchSlide){
22660             this.el.on("touchstart", this.onTouchStart, this);
22661             this.el.on("touchmove", this.onTouchMove, this);
22662             this.el.on("touchend", this.onTouchEnd, this);
22663         }
22664         
22665     },
22666     
22667     onRender : function(ct, position)
22668     {
22669         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22670     },
22671     
22672     setActive : function(state)
22673     {
22674         Roo.log("panel - set active " + this.tabId + "=" + state);
22675         
22676         this.active = state;
22677         if (!state) {
22678             this.el.removeClass('active');
22679             
22680         } else  if (!this.el.hasClass('active')) {
22681             this.el.addClass('active');
22682         }
22683         
22684         this.fireEvent('changed', this, state);
22685     },
22686     
22687     onClick : function(e)
22688     {
22689         e.preventDefault();
22690         
22691         if(!this.href.length){
22692             return;
22693         }
22694         
22695         window.location.href = this.href;
22696     },
22697     
22698     startX : 0,
22699     startY : 0,
22700     endX : 0,
22701     endY : 0,
22702     swiping : false,
22703     
22704     onTouchStart : function(e)
22705     {
22706         this.swiping = false;
22707         
22708         this.startX = e.browserEvent.touches[0].clientX;
22709         this.startY = e.browserEvent.touches[0].clientY;
22710     },
22711     
22712     onTouchMove : function(e)
22713     {
22714         this.swiping = true;
22715         
22716         this.endX = e.browserEvent.touches[0].clientX;
22717         this.endY = e.browserEvent.touches[0].clientY;
22718     },
22719     
22720     onTouchEnd : function(e)
22721     {
22722         if(!this.swiping){
22723             this.onClick(e);
22724             return;
22725         }
22726         
22727         var tabGroup = this.parent();
22728         
22729         if(this.endX > this.startX){ // swiping right
22730             tabGroup.showPanelPrev();
22731             return;
22732         }
22733         
22734         if(this.startX > this.endX){ // swiping left
22735             tabGroup.showPanelNext();
22736             return;
22737         }
22738     }
22739     
22740     
22741 });
22742  
22743
22744  
22745
22746  /*
22747  * - LGPL
22748  *
22749  * DateField
22750  * 
22751  */
22752
22753 /**
22754  * @class Roo.bootstrap.form.DateField
22755  * @extends Roo.bootstrap.form.Input
22756  * Bootstrap DateField class
22757  * @cfg {Number} weekStart default 0
22758  * @cfg {String} viewMode default empty, (months|years)
22759  * @cfg {String} minViewMode default empty, (months|years)
22760  * @cfg {Number} startDate default -Infinity
22761  * @cfg {Number} endDate default Infinity
22762  * @cfg {Boolean} todayHighlight default false
22763  * @cfg {Boolean} todayBtn default false
22764  * @cfg {Boolean} calendarWeeks default false
22765  * @cfg {Object} daysOfWeekDisabled default empty
22766  * @cfg {Boolean} singleMode default false (true | false)
22767  * 
22768  * @cfg {Boolean} keyboardNavigation default true
22769  * @cfg {String} language default en
22770  * 
22771  * @constructor
22772  * Create a new DateField
22773  * @param {Object} config The config object
22774  */
22775
22776 Roo.bootstrap.form.DateField = function(config){
22777     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22778      this.addEvents({
22779             /**
22780              * @event show
22781              * Fires when this field show.
22782              * @param {Roo.bootstrap.form.DateField} this
22783              * @param {Mixed} date The date value
22784              */
22785             show : true,
22786             /**
22787              * @event show
22788              * Fires when this field hide.
22789              * @param {Roo.bootstrap.form.DateField} this
22790              * @param {Mixed} date The date value
22791              */
22792             hide : true,
22793             /**
22794              * @event select
22795              * Fires when select a date.
22796              * @param {Roo.bootstrap.form.DateField} this
22797              * @param {Mixed} date The date value
22798              */
22799             select : true,
22800             /**
22801              * @event beforeselect
22802              * Fires when before select a date.
22803              * @param {Roo.bootstrap.form.DateField} this
22804              * @param {Mixed} date The date value
22805              */
22806             beforeselect : true
22807         });
22808 };
22809
22810 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22811     
22812     /**
22813      * @cfg {String} format
22814      * The default date format string which can be overriden for localization support.  The format must be
22815      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22816      */
22817     format : "m/d/y",
22818     /**
22819      * @cfg {String} altFormats
22820      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22821      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22822      */
22823     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22824     
22825     weekStart : 0,
22826     
22827     viewMode : '',
22828     
22829     minViewMode : '',
22830     
22831     todayHighlight : false,
22832     
22833     todayBtn: false,
22834     
22835     language: 'en',
22836     
22837     keyboardNavigation: true,
22838     
22839     calendarWeeks: false,
22840     
22841     startDate: -Infinity,
22842     
22843     endDate: Infinity,
22844     
22845     daysOfWeekDisabled: [],
22846     
22847     _events: [],
22848     
22849     singleMode : false,
22850     
22851     UTCDate: function()
22852     {
22853         return new Date(Date.UTC.apply(Date, arguments));
22854     },
22855     
22856     UTCToday: function()
22857     {
22858         var today = new Date();
22859         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22860     },
22861     
22862     getDate: function() {
22863             var d = this.getUTCDate();
22864             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22865     },
22866     
22867     getUTCDate: function() {
22868             return this.date;
22869     },
22870     
22871     setDate: function(d) {
22872             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22873     },
22874     
22875     setUTCDate: function(d) {
22876             this.date = d;
22877             this.setValue(this.formatDate(this.date));
22878     },
22879         
22880     onRender: function(ct, position)
22881     {
22882         
22883         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22884         
22885         this.language = this.language || 'en';
22886         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22887         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22888         
22889         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22890         this.format = this.format || 'm/d/y';
22891         this.isInline = false;
22892         this.isInput = true;
22893         this.component = this.el.select('.add-on', true).first() || false;
22894         this.component = (this.component && this.component.length === 0) ? false : this.component;
22895         this.hasInput = this.component && this.inputEl().length;
22896         
22897         if (typeof(this.minViewMode === 'string')) {
22898             switch (this.minViewMode) {
22899                 case 'months':
22900                     this.minViewMode = 1;
22901                     break;
22902                 case 'years':
22903                     this.minViewMode = 2;
22904                     break;
22905                 default:
22906                     this.minViewMode = 0;
22907                     break;
22908             }
22909         }
22910         
22911         if (typeof(this.viewMode === 'string')) {
22912             switch (this.viewMode) {
22913                 case 'months':
22914                     this.viewMode = 1;
22915                     break;
22916                 case 'years':
22917                     this.viewMode = 2;
22918                     break;
22919                 default:
22920                     this.viewMode = 0;
22921                     break;
22922             }
22923         }
22924                 
22925         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22926         
22927 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22928         
22929         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22930         
22931         this.picker().on('mousedown', this.onMousedown, this);
22932         this.picker().on('click', this.onClick, this);
22933         
22934         this.picker().addClass('datepicker-dropdown');
22935         
22936         this.startViewMode = this.viewMode;
22937         
22938         if(this.singleMode){
22939             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22940                 v.setVisibilityMode(Roo.Element.DISPLAY);
22941                 v.hide();
22942             });
22943             
22944             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22945                 v.setStyle('width', '189px');
22946             });
22947         }
22948         
22949         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22950             if(!this.calendarWeeks){
22951                 v.remove();
22952                 return;
22953             }
22954             
22955             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22956             v.attr('colspan', function(i, val){
22957                 return parseInt(val) + 1;
22958             });
22959         });
22960                         
22961         
22962         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22963         
22964         this.setStartDate(this.startDate);
22965         this.setEndDate(this.endDate);
22966         
22967         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22968         
22969         this.fillDow();
22970         this.fillMonths();
22971         this.update();
22972         this.showMode();
22973         
22974         if(this.isInline) {
22975             this.showPopup();
22976         }
22977     },
22978     
22979     picker : function()
22980     {
22981         return this.pickerEl;
22982 //        return this.el.select('.datepicker', true).first();
22983     },
22984     
22985     fillDow: function()
22986     {
22987         var dowCnt = this.weekStart;
22988         
22989         var dow = {
22990             tag: 'tr',
22991             cn: [
22992                 
22993             ]
22994         };
22995         
22996         if(this.calendarWeeks){
22997             dow.cn.push({
22998                 tag: 'th',
22999                 cls: 'cw',
23000                 html: '&nbsp;'
23001             })
23002         }
23003         
23004         while (dowCnt < this.weekStart + 7) {
23005             dow.cn.push({
23006                 tag: 'th',
23007                 cls: 'dow',
23008                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23009             });
23010         }
23011         
23012         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23013     },
23014     
23015     fillMonths: function()
23016     {    
23017         var i = 0;
23018         var months = this.picker().select('>.datepicker-months td', true).first();
23019         
23020         months.dom.innerHTML = '';
23021         
23022         while (i < 12) {
23023             var month = {
23024                 tag: 'span',
23025                 cls: 'month',
23026                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23027             };
23028             
23029             months.createChild(month);
23030         }
23031         
23032     },
23033     
23034     update: function()
23035     {
23036         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;
23037         
23038         if (this.date < this.startDate) {
23039             this.viewDate = new Date(this.startDate);
23040         } else if (this.date > this.endDate) {
23041             this.viewDate = new Date(this.endDate);
23042         } else {
23043             this.viewDate = new Date(this.date);
23044         }
23045         
23046         this.fill();
23047     },
23048     
23049     fill: function() 
23050     {
23051         var d = new Date(this.viewDate),
23052                 year = d.getUTCFullYear(),
23053                 month = d.getUTCMonth(),
23054                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23055                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23056                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23057                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23058                 currentDate = this.date && this.date.valueOf(),
23059                 today = this.UTCToday();
23060         
23061         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23062         
23063 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23064         
23065 //        this.picker.select('>tfoot th.today').
23066 //                                              .text(dates[this.language].today)
23067 //                                              .toggle(this.todayBtn !== false);
23068     
23069         this.updateNavArrows();
23070         this.fillMonths();
23071                                                 
23072         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23073         
23074         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23075          
23076         prevMonth.setUTCDate(day);
23077         
23078         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23079         
23080         var nextMonth = new Date(prevMonth);
23081         
23082         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23083         
23084         nextMonth = nextMonth.valueOf();
23085         
23086         var fillMonths = false;
23087         
23088         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23089         
23090         while(prevMonth.valueOf() <= nextMonth) {
23091             var clsName = '';
23092             
23093             if (prevMonth.getUTCDay() === this.weekStart) {
23094                 if(fillMonths){
23095                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23096                 }
23097                     
23098                 fillMonths = {
23099                     tag: 'tr',
23100                     cn: []
23101                 };
23102                 
23103                 if(this.calendarWeeks){
23104                     // ISO 8601: First week contains first thursday.
23105                     // ISO also states week starts on Monday, but we can be more abstract here.
23106                     var
23107                     // Start of current week: based on weekstart/current date
23108                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23109                     // Thursday of this week
23110                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23111                     // First Thursday of year, year from thursday
23112                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23113                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23114                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23115                     
23116                     fillMonths.cn.push({
23117                         tag: 'td',
23118                         cls: 'cw',
23119                         html: calWeek
23120                     });
23121                 }
23122             }
23123             
23124             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23125                 clsName += ' old';
23126             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23127                 clsName += ' new';
23128             }
23129             if (this.todayHighlight &&
23130                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23131                 prevMonth.getUTCMonth() == today.getMonth() &&
23132                 prevMonth.getUTCDate() == today.getDate()) {
23133                 clsName += ' today';
23134             }
23135             
23136             if (currentDate && prevMonth.valueOf() === currentDate) {
23137                 clsName += ' active';
23138             }
23139             
23140             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23141                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23142                     clsName += ' disabled';
23143             }
23144             
23145             fillMonths.cn.push({
23146                 tag: 'td',
23147                 cls: 'day ' + clsName,
23148                 html: prevMonth.getDate()
23149             });
23150             
23151             prevMonth.setDate(prevMonth.getDate()+1);
23152         }
23153           
23154         var currentYear = this.date && this.date.getUTCFullYear();
23155         var currentMonth = this.date && this.date.getUTCMonth();
23156         
23157         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23158         
23159         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23160             v.removeClass('active');
23161             
23162             if(currentYear === year && k === currentMonth){
23163                 v.addClass('active');
23164             }
23165             
23166             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23167                 v.addClass('disabled');
23168             }
23169             
23170         });
23171         
23172         
23173         year = parseInt(year/10, 10) * 10;
23174         
23175         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23176         
23177         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23178         
23179         year -= 1;
23180         for (var i = -1; i < 11; i++) {
23181             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23182                 tag: 'span',
23183                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23184                 html: year
23185             });
23186             
23187             year += 1;
23188         }
23189     },
23190     
23191     showMode: function(dir) 
23192     {
23193         if (dir) {
23194             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23195         }
23196         
23197         Roo.each(this.picker().select('>div',true).elements, function(v){
23198             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23199             v.hide();
23200         });
23201         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23202     },
23203     
23204     place: function()
23205     {
23206         if(this.isInline) {
23207             return;
23208         }
23209         
23210         this.picker().removeClass(['bottom', 'top']);
23211         
23212         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23213             /*
23214              * place to the top of element!
23215              *
23216              */
23217             
23218             this.picker().addClass('top');
23219             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23220             
23221             return;
23222         }
23223         
23224         this.picker().addClass('bottom');
23225         
23226         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23227     },
23228     
23229     parseDate : function(value)
23230     {
23231         if(!value || value instanceof Date){
23232             return value;
23233         }
23234         var v = Date.parseDate(value, this.format);
23235         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23236             v = Date.parseDate(value, 'Y-m-d');
23237         }
23238         if(!v && this.altFormats){
23239             if(!this.altFormatsArray){
23240                 this.altFormatsArray = this.altFormats.split("|");
23241             }
23242             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23243                 v = Date.parseDate(value, this.altFormatsArray[i]);
23244             }
23245         }
23246         return v;
23247     },
23248     
23249     formatDate : function(date, fmt)
23250     {   
23251         return (!date || !(date instanceof Date)) ?
23252         date : date.dateFormat(fmt || this.format);
23253     },
23254     
23255     onFocus : function()
23256     {
23257         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23258         this.showPopup();
23259     },
23260     
23261     onBlur : function()
23262     {
23263         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23264         
23265         var d = this.inputEl().getValue();
23266         
23267         this.setValue(d);
23268                 
23269         this.hidePopup();
23270     },
23271     
23272     showPopup : function()
23273     {
23274         this.picker().show();
23275         this.update();
23276         this.place();
23277         
23278         this.fireEvent('showpopup', this, this.date);
23279     },
23280     
23281     hidePopup : function()
23282     {
23283         if(this.isInline) {
23284             return;
23285         }
23286         this.picker().hide();
23287         this.viewMode = this.startViewMode;
23288         this.showMode();
23289         
23290         this.fireEvent('hidepopup', this, this.date);
23291         
23292     },
23293     
23294     onMousedown: function(e)
23295     {
23296         e.stopPropagation();
23297         e.preventDefault();
23298     },
23299     
23300     keyup: function(e)
23301     {
23302         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23303         this.update();
23304     },
23305
23306     setValue: function(v)
23307     {
23308         if(this.fireEvent('beforeselect', this, v) !== false){
23309             var d = new Date(this.parseDate(v) ).clearTime();
23310         
23311             if(isNaN(d.getTime())){
23312                 this.date = this.viewDate = '';
23313                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23314                 return;
23315             }
23316
23317             v = this.formatDate(d);
23318
23319             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23320
23321             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23322
23323             this.update();
23324
23325             this.fireEvent('select', this, this.date);
23326         }
23327     },
23328     
23329     getValue: function()
23330     {
23331         return this.formatDate(this.date);
23332     },
23333     
23334     fireKey: function(e)
23335     {
23336         if (!this.picker().isVisible()){
23337             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23338                 this.showPopup();
23339             }
23340             return;
23341         }
23342         
23343         var dateChanged = false,
23344         dir, day, month,
23345         newDate, newViewDate;
23346         
23347         switch(e.keyCode){
23348             case 27: // escape
23349                 this.hidePopup();
23350                 e.preventDefault();
23351                 break;
23352             case 37: // left
23353             case 39: // right
23354                 if (!this.keyboardNavigation) {
23355                     break;
23356                 }
23357                 dir = e.keyCode == 37 ? -1 : 1;
23358                 
23359                 if (e.ctrlKey){
23360                     newDate = this.moveYear(this.date, dir);
23361                     newViewDate = this.moveYear(this.viewDate, dir);
23362                 } else if (e.shiftKey){
23363                     newDate = this.moveMonth(this.date, dir);
23364                     newViewDate = this.moveMonth(this.viewDate, dir);
23365                 } else {
23366                     newDate = new Date(this.date);
23367                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23368                     newViewDate = new Date(this.viewDate);
23369                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23370                 }
23371                 if (this.dateWithinRange(newDate)){
23372                     this.date = newDate;
23373                     this.viewDate = newViewDate;
23374                     this.setValue(this.formatDate(this.date));
23375 //                    this.update();
23376                     e.preventDefault();
23377                     dateChanged = true;
23378                 }
23379                 break;
23380             case 38: // up
23381             case 40: // down
23382                 if (!this.keyboardNavigation) {
23383                     break;
23384                 }
23385                 dir = e.keyCode == 38 ? -1 : 1;
23386                 if (e.ctrlKey){
23387                     newDate = this.moveYear(this.date, dir);
23388                     newViewDate = this.moveYear(this.viewDate, dir);
23389                 } else if (e.shiftKey){
23390                     newDate = this.moveMonth(this.date, dir);
23391                     newViewDate = this.moveMonth(this.viewDate, dir);
23392                 } else {
23393                     newDate = new Date(this.date);
23394                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23395                     newViewDate = new Date(this.viewDate);
23396                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23397                 }
23398                 if (this.dateWithinRange(newDate)){
23399                     this.date = newDate;
23400                     this.viewDate = newViewDate;
23401                     this.setValue(this.formatDate(this.date));
23402 //                    this.update();
23403                     e.preventDefault();
23404                     dateChanged = true;
23405                 }
23406                 break;
23407             case 13: // enter
23408                 this.setValue(this.formatDate(this.date));
23409                 this.hidePopup();
23410                 e.preventDefault();
23411                 break;
23412             case 9: // tab
23413                 this.setValue(this.formatDate(this.date));
23414                 this.hidePopup();
23415                 break;
23416             case 16: // shift
23417             case 17: // ctrl
23418             case 18: // alt
23419                 break;
23420             default :
23421                 this.hidePopup();
23422                 
23423         }
23424     },
23425     
23426     
23427     onClick: function(e) 
23428     {
23429         e.stopPropagation();
23430         e.preventDefault();
23431         
23432         var target = e.getTarget();
23433         
23434         if(target.nodeName.toLowerCase() === 'i'){
23435             target = Roo.get(target).dom.parentNode;
23436         }
23437         
23438         var nodeName = target.nodeName;
23439         var className = target.className;
23440         var html = target.innerHTML;
23441         //Roo.log(nodeName);
23442         
23443         switch(nodeName.toLowerCase()) {
23444             case 'th':
23445                 switch(className) {
23446                     case 'switch':
23447                         this.showMode(1);
23448                         break;
23449                     case 'prev':
23450                     case 'next':
23451                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23452                         switch(this.viewMode){
23453                                 case 0:
23454                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23455                                         break;
23456                                 case 1:
23457                                 case 2:
23458                                         this.viewDate = this.moveYear(this.viewDate, dir);
23459                                         break;
23460                         }
23461                         this.fill();
23462                         break;
23463                     case 'today':
23464                         var date = new Date();
23465                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23466 //                        this.fill()
23467                         this.setValue(this.formatDate(this.date));
23468                         
23469                         this.hidePopup();
23470                         break;
23471                 }
23472                 break;
23473             case 'span':
23474                 if (className.indexOf('disabled') < 0) {
23475                 if (!this.viewDate) {
23476                     this.viewDate = new Date();
23477                 }
23478                 this.viewDate.setUTCDate(1);
23479                     if (className.indexOf('month') > -1) {
23480                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23481                     } else {
23482                         var year = parseInt(html, 10) || 0;
23483                         this.viewDate.setUTCFullYear(year);
23484                         
23485                     }
23486                     
23487                     if(this.singleMode){
23488                         this.setValue(this.formatDate(this.viewDate));
23489                         this.hidePopup();
23490                         return;
23491                     }
23492                     
23493                     this.showMode(-1);
23494                     this.fill();
23495                 }
23496                 break;
23497                 
23498             case 'td':
23499                 //Roo.log(className);
23500                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23501                     var day = parseInt(html, 10) || 1;
23502                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23503                         month = (this.viewDate || new Date()).getUTCMonth();
23504
23505                     if (className.indexOf('old') > -1) {
23506                         if(month === 0 ){
23507                             month = 11;
23508                             year -= 1;
23509                         }else{
23510                             month -= 1;
23511                         }
23512                     } else if (className.indexOf('new') > -1) {
23513                         if (month == 11) {
23514                             month = 0;
23515                             year += 1;
23516                         } else {
23517                             month += 1;
23518                         }
23519                     }
23520                     //Roo.log([year,month,day]);
23521                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23522                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23523 //                    this.fill();
23524                     //Roo.log(this.formatDate(this.date));
23525                     this.setValue(this.formatDate(this.date));
23526                     this.hidePopup();
23527                 }
23528                 break;
23529         }
23530     },
23531     
23532     setStartDate: function(startDate)
23533     {
23534         this.startDate = startDate || -Infinity;
23535         if (this.startDate !== -Infinity) {
23536             this.startDate = this.parseDate(this.startDate);
23537         }
23538         this.update();
23539         this.updateNavArrows();
23540     },
23541
23542     setEndDate: function(endDate)
23543     {
23544         this.endDate = endDate || Infinity;
23545         if (this.endDate !== Infinity) {
23546             this.endDate = this.parseDate(this.endDate);
23547         }
23548         this.update();
23549         this.updateNavArrows();
23550     },
23551     
23552     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23553     {
23554         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23555         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23556             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23557         }
23558         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23559             return parseInt(d, 10);
23560         });
23561         this.update();
23562         this.updateNavArrows();
23563     },
23564     
23565     updateNavArrows: function() 
23566     {
23567         if(this.singleMode){
23568             return;
23569         }
23570         
23571         var d = new Date(this.viewDate),
23572         year = d.getUTCFullYear(),
23573         month = d.getUTCMonth();
23574         
23575         Roo.each(this.picker().select('.prev', true).elements, function(v){
23576             v.show();
23577             switch (this.viewMode) {
23578                 case 0:
23579
23580                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23581                         v.hide();
23582                     }
23583                     break;
23584                 case 1:
23585                 case 2:
23586                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23587                         v.hide();
23588                     }
23589                     break;
23590             }
23591         });
23592         
23593         Roo.each(this.picker().select('.next', true).elements, function(v){
23594             v.show();
23595             switch (this.viewMode) {
23596                 case 0:
23597
23598                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23599                         v.hide();
23600                     }
23601                     break;
23602                 case 1:
23603                 case 2:
23604                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23605                         v.hide();
23606                     }
23607                     break;
23608             }
23609         })
23610     },
23611     
23612     moveMonth: function(date, dir)
23613     {
23614         if (!dir) {
23615             return date;
23616         }
23617         var new_date = new Date(date.valueOf()),
23618         day = new_date.getUTCDate(),
23619         month = new_date.getUTCMonth(),
23620         mag = Math.abs(dir),
23621         new_month, test;
23622         dir = dir > 0 ? 1 : -1;
23623         if (mag == 1){
23624             test = dir == -1
23625             // If going back one month, make sure month is not current month
23626             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23627             ? function(){
23628                 return new_date.getUTCMonth() == month;
23629             }
23630             // If going forward one month, make sure month is as expected
23631             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23632             : function(){
23633                 return new_date.getUTCMonth() != new_month;
23634             };
23635             new_month = month + dir;
23636             new_date.setUTCMonth(new_month);
23637             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23638             if (new_month < 0 || new_month > 11) {
23639                 new_month = (new_month + 12) % 12;
23640             }
23641         } else {
23642             // For magnitudes >1, move one month at a time...
23643             for (var i=0; i<mag; i++) {
23644                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23645                 new_date = this.moveMonth(new_date, dir);
23646             }
23647             // ...then reset the day, keeping it in the new month
23648             new_month = new_date.getUTCMonth();
23649             new_date.setUTCDate(day);
23650             test = function(){
23651                 return new_month != new_date.getUTCMonth();
23652             };
23653         }
23654         // Common date-resetting loop -- if date is beyond end of month, make it
23655         // end of month
23656         while (test()){
23657             new_date.setUTCDate(--day);
23658             new_date.setUTCMonth(new_month);
23659         }
23660         return new_date;
23661     },
23662
23663     moveYear: function(date, dir)
23664     {
23665         return this.moveMonth(date, dir*12);
23666     },
23667
23668     dateWithinRange: function(date)
23669     {
23670         return date >= this.startDate && date <= this.endDate;
23671     },
23672
23673     
23674     remove: function() 
23675     {
23676         this.picker().remove();
23677     },
23678     
23679     validateValue : function(value)
23680     {
23681         if(this.getVisibilityEl().hasClass('hidden')){
23682             return true;
23683         }
23684         
23685         if(value.length < 1)  {
23686             if(this.allowBlank){
23687                 return true;
23688             }
23689             return false;
23690         }
23691         
23692         if(value.length < this.minLength){
23693             return false;
23694         }
23695         if(value.length > this.maxLength){
23696             return false;
23697         }
23698         if(this.vtype){
23699             var vt = Roo.form.VTypes;
23700             if(!vt[this.vtype](value, this)){
23701                 return false;
23702             }
23703         }
23704         if(typeof this.validator == "function"){
23705             var msg = this.validator(value);
23706             if(msg !== true){
23707                 return false;
23708             }
23709         }
23710         
23711         if(this.regex && !this.regex.test(value)){
23712             return false;
23713         }
23714         
23715         if(typeof(this.parseDate(value)) == 'undefined'){
23716             return false;
23717         }
23718         
23719         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23720             return false;
23721         }      
23722         
23723         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23724             return false;
23725         } 
23726         
23727         
23728         return true;
23729     },
23730     
23731     reset : function()
23732     {
23733         this.date = this.viewDate = '';
23734         
23735         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23736     }
23737    
23738 });
23739
23740 Roo.apply(Roo.bootstrap.form.DateField,  {
23741     
23742     head : {
23743         tag: 'thead',
23744         cn: [
23745         {
23746             tag: 'tr',
23747             cn: [
23748             {
23749                 tag: 'th',
23750                 cls: 'prev',
23751                 html: '<i class="fa fa-arrow-left"/>'
23752             },
23753             {
23754                 tag: 'th',
23755                 cls: 'switch',
23756                 colspan: '5'
23757             },
23758             {
23759                 tag: 'th',
23760                 cls: 'next',
23761                 html: '<i class="fa fa-arrow-right"/>'
23762             }
23763
23764             ]
23765         }
23766         ]
23767     },
23768     
23769     content : {
23770         tag: 'tbody',
23771         cn: [
23772         {
23773             tag: 'tr',
23774             cn: [
23775             {
23776                 tag: 'td',
23777                 colspan: '7'
23778             }
23779             ]
23780         }
23781         ]
23782     },
23783     
23784     footer : {
23785         tag: 'tfoot',
23786         cn: [
23787         {
23788             tag: 'tr',
23789             cn: [
23790             {
23791                 tag: 'th',
23792                 colspan: '7',
23793                 cls: 'today'
23794             }
23795                     
23796             ]
23797         }
23798         ]
23799     },
23800     
23801     dates:{
23802         en: {
23803             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23804             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23805             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23806             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23807             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23808             today: "Today"
23809         }
23810     },
23811     
23812     modes: [
23813     {
23814         clsName: 'days',
23815         navFnc: 'Month',
23816         navStep: 1
23817     },
23818     {
23819         clsName: 'months',
23820         navFnc: 'FullYear',
23821         navStep: 1
23822     },
23823     {
23824         clsName: 'years',
23825         navFnc: 'FullYear',
23826         navStep: 10
23827     }]
23828 });
23829
23830 Roo.apply(Roo.bootstrap.form.DateField,  {
23831   
23832     template : {
23833         tag: 'div',
23834         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23835         cn: [
23836         {
23837             tag: 'div',
23838             cls: 'datepicker-days',
23839             cn: [
23840             {
23841                 tag: 'table',
23842                 cls: 'table-condensed',
23843                 cn:[
23844                 Roo.bootstrap.form.DateField.head,
23845                 {
23846                     tag: 'tbody'
23847                 },
23848                 Roo.bootstrap.form.DateField.footer
23849                 ]
23850             }
23851             ]
23852         },
23853         {
23854             tag: 'div',
23855             cls: 'datepicker-months',
23856             cn: [
23857             {
23858                 tag: 'table',
23859                 cls: 'table-condensed',
23860                 cn:[
23861                 Roo.bootstrap.form.DateField.head,
23862                 Roo.bootstrap.form.DateField.content,
23863                 Roo.bootstrap.form.DateField.footer
23864                 ]
23865             }
23866             ]
23867         },
23868         {
23869             tag: 'div',
23870             cls: 'datepicker-years',
23871             cn: [
23872             {
23873                 tag: 'table',
23874                 cls: 'table-condensed',
23875                 cn:[
23876                 Roo.bootstrap.form.DateField.head,
23877                 Roo.bootstrap.form.DateField.content,
23878                 Roo.bootstrap.form.DateField.footer
23879                 ]
23880             }
23881             ]
23882         }
23883         ]
23884     }
23885 });
23886
23887  
23888
23889  /*
23890  * - LGPL
23891  *
23892  * TimeField
23893  * 
23894  */
23895
23896 /**
23897  * @class Roo.bootstrap.form.TimeField
23898  * @extends Roo.bootstrap.form.Input
23899  * Bootstrap DateField class
23900  * 
23901  * 
23902  * @constructor
23903  * Create a new TimeField
23904  * @param {Object} config The config object
23905  */
23906
23907 Roo.bootstrap.form.TimeField = function(config){
23908     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23909     this.addEvents({
23910             /**
23911              * @event show
23912              * Fires when this field show.
23913              * @param {Roo.bootstrap.form.DateField} thisthis
23914              * @param {Mixed} date The date value
23915              */
23916             show : true,
23917             /**
23918              * @event show
23919              * Fires when this field hide.
23920              * @param {Roo.bootstrap.form.DateField} this
23921              * @param {Mixed} date The date value
23922              */
23923             hide : true,
23924             /**
23925              * @event select
23926              * Fires when select a date.
23927              * @param {Roo.bootstrap.form.DateField} this
23928              * @param {Mixed} date The date value
23929              */
23930             select : true
23931         });
23932 };
23933
23934 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23935     
23936     /**
23937      * @cfg {String} format
23938      * The default time format string which can be overriden for localization support.  The format must be
23939      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23940      */
23941     format : "H:i",
23942
23943     getAutoCreate : function()
23944     {
23945         this.after = '<i class="fa far fa-clock"></i>';
23946         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23947         
23948          
23949     },
23950     onRender: function(ct, position)
23951     {
23952         
23953         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23954                 
23955         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23956         
23957         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23958         
23959         this.pop = this.picker().select('>.datepicker-time',true).first();
23960         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23961         
23962         this.picker().on('mousedown', this.onMousedown, this);
23963         this.picker().on('click', this.onClick, this);
23964         
23965         this.picker().addClass('datepicker-dropdown');
23966     
23967         this.fillTime();
23968         this.update();
23969             
23970         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23971         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23972         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23973         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23974         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23975         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23976
23977     },
23978     
23979     fireKey: function(e){
23980         if (!this.picker().isVisible()){
23981             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23982                 this.show();
23983             }
23984             return;
23985         }
23986
23987         e.preventDefault();
23988         
23989         switch(e.keyCode){
23990             case 27: // escape
23991                 this.hide();
23992                 break;
23993             case 37: // left
23994             case 39: // right
23995                 this.onTogglePeriod();
23996                 break;
23997             case 38: // up
23998                 this.onIncrementMinutes();
23999                 break;
24000             case 40: // down
24001                 this.onDecrementMinutes();
24002                 break;
24003             case 13: // enter
24004             case 9: // tab
24005                 this.setTime();
24006                 break;
24007         }
24008     },
24009     
24010     onClick: function(e) {
24011         e.stopPropagation();
24012         e.preventDefault();
24013     },
24014     
24015     picker : function()
24016     {
24017         return this.pickerEl;
24018     },
24019     
24020     fillTime: function()
24021     {    
24022         var time = this.pop.select('tbody', true).first();
24023         
24024         time.dom.innerHTML = '';
24025         
24026         time.createChild({
24027             tag: 'tr',
24028             cn: [
24029                 {
24030                     tag: 'td',
24031                     cn: [
24032                         {
24033                             tag: 'a',
24034                             href: '#',
24035                             cls: 'btn',
24036                             cn: [
24037                                 {
24038                                     tag: 'i',
24039                                     cls: 'hours-up fa fas fa-chevron-up'
24040                                 }
24041                             ]
24042                         } 
24043                     ]
24044                 },
24045                 {
24046                     tag: 'td',
24047                     cls: 'separator'
24048                 },
24049                 {
24050                     tag: 'td',
24051                     cn: [
24052                         {
24053                             tag: 'a',
24054                             href: '#',
24055                             cls: 'btn',
24056                             cn: [
24057                                 {
24058                                     tag: 'i',
24059                                     cls: 'minutes-up fa fas fa-chevron-up'
24060                                 }
24061                             ]
24062                         }
24063                     ]
24064                 },
24065                 {
24066                     tag: 'td',
24067                     cls: 'separator'
24068                 }
24069             ]
24070         });
24071         
24072         time.createChild({
24073             tag: 'tr',
24074             cn: [
24075                 {
24076                     tag: 'td',
24077                     cn: [
24078                         {
24079                             tag: 'span',
24080                             cls: 'timepicker-hour',
24081                             html: '00'
24082                         }  
24083                     ]
24084                 },
24085                 {
24086                     tag: 'td',
24087                     cls: 'separator',
24088                     html: ':'
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cn: [
24093                         {
24094                             tag: 'span',
24095                             cls: 'timepicker-minute',
24096                             html: '00'
24097                         }  
24098                     ]
24099                 },
24100                 {
24101                     tag: 'td',
24102                     cls: 'separator'
24103                 },
24104                 {
24105                     tag: 'td',
24106                     cn: [
24107                         {
24108                             tag: 'button',
24109                             type: 'button',
24110                             cls: 'btn btn-primary period',
24111                             html: 'AM'
24112                             
24113                         }
24114                     ]
24115                 }
24116             ]
24117         });
24118         
24119         time.createChild({
24120             tag: 'tr',
24121             cn: [
24122                 {
24123                     tag: 'td',
24124                     cn: [
24125                         {
24126                             tag: 'a',
24127                             href: '#',
24128                             cls: 'btn',
24129                             cn: [
24130                                 {
24131                                     tag: 'span',
24132                                     cls: 'hours-down fa fas fa-chevron-down'
24133                                 }
24134                             ]
24135                         }
24136                     ]
24137                 },
24138                 {
24139                     tag: 'td',
24140                     cls: 'separator'
24141                 },
24142                 {
24143                     tag: 'td',
24144                     cn: [
24145                         {
24146                             tag: 'a',
24147                             href: '#',
24148                             cls: 'btn',
24149                             cn: [
24150                                 {
24151                                     tag: 'span',
24152                                     cls: 'minutes-down fa fas fa-chevron-down'
24153                                 }
24154                             ]
24155                         }
24156                     ]
24157                 },
24158                 {
24159                     tag: 'td',
24160                     cls: 'separator'
24161                 }
24162             ]
24163         });
24164         
24165     },
24166     
24167     update: function()
24168     {
24169         
24170         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24171         
24172         this.fill();
24173     },
24174     
24175     fill: function() 
24176     {
24177         var hours = this.time.getHours();
24178         var minutes = this.time.getMinutes();
24179         var period = 'AM';
24180         
24181         if(hours > 11){
24182             period = 'PM';
24183         }
24184         
24185         if(hours == 0){
24186             hours = 12;
24187         }
24188         
24189         
24190         if(hours > 12){
24191             hours = hours - 12;
24192         }
24193         
24194         if(hours < 10){
24195             hours = '0' + hours;
24196         }
24197         
24198         if(minutes < 10){
24199             minutes = '0' + minutes;
24200         }
24201         
24202         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24203         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24204         this.pop.select('button', true).first().dom.innerHTML = period;
24205         
24206     },
24207     
24208     place: function()
24209     {   
24210         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24211         
24212         var cls = ['bottom'];
24213         
24214         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24215             cls.pop();
24216             cls.push('top');
24217         }
24218         
24219         cls.push('right');
24220         
24221         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24222             cls.pop();
24223             cls.push('left');
24224         }
24225         //this.picker().setXY(20000,20000);
24226         this.picker().addClass(cls.join('-'));
24227         
24228         var _this = this;
24229         
24230         Roo.each(cls, function(c){
24231             if(c == 'bottom'){
24232                 (function() {
24233                  //  
24234                 }).defer(200);
24235                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24236                 //_this.picker().setTop(_this.inputEl().getHeight());
24237                 return;
24238             }
24239             if(c == 'top'){
24240                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24241                 
24242                 //_this.picker().setTop(0 - _this.picker().getHeight());
24243                 return;
24244             }
24245             /*
24246             if(c == 'left'){
24247                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24248                 return;
24249             }
24250             if(c == 'right'){
24251                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24252                 return;
24253             }
24254             */
24255         });
24256         
24257     },
24258   
24259     onFocus : function()
24260     {
24261         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24262         this.show();
24263     },
24264     
24265     onBlur : function()
24266     {
24267         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24268         this.hide();
24269     },
24270     
24271     show : function()
24272     {
24273         this.picker().show();
24274         this.pop.show();
24275         this.update();
24276         this.place();
24277         
24278         this.fireEvent('show', this, this.date);
24279     },
24280     
24281     hide : function()
24282     {
24283         this.picker().hide();
24284         this.pop.hide();
24285         
24286         this.fireEvent('hide', this, this.date);
24287     },
24288     
24289     setTime : function()
24290     {
24291         this.hide();
24292         this.setValue(this.time.format(this.format));
24293         
24294         this.fireEvent('select', this, this.date);
24295         
24296         
24297     },
24298     
24299     onMousedown: function(e){
24300         e.stopPropagation();
24301         e.preventDefault();
24302     },
24303     
24304     onIncrementHours: function()
24305     {
24306         Roo.log('onIncrementHours');
24307         this.time = this.time.add(Date.HOUR, 1);
24308         this.update();
24309         
24310     },
24311     
24312     onDecrementHours: function()
24313     {
24314         Roo.log('onDecrementHours');
24315         this.time = this.time.add(Date.HOUR, -1);
24316         this.update();
24317     },
24318     
24319     onIncrementMinutes: function()
24320     {
24321         Roo.log('onIncrementMinutes');
24322         this.time = this.time.add(Date.MINUTE, 1);
24323         this.update();
24324     },
24325     
24326     onDecrementMinutes: function()
24327     {
24328         Roo.log('onDecrementMinutes');
24329         this.time = this.time.add(Date.MINUTE, -1);
24330         this.update();
24331     },
24332     
24333     onTogglePeriod: function()
24334     {
24335         Roo.log('onTogglePeriod');
24336         this.time = this.time.add(Date.HOUR, 12);
24337         this.update();
24338     }
24339     
24340    
24341 });
24342  
24343
24344 Roo.apply(Roo.bootstrap.form.TimeField,  {
24345   
24346     template : {
24347         tag: 'div',
24348         cls: 'datepicker dropdown-menu',
24349         cn: [
24350             {
24351                 tag: 'div',
24352                 cls: 'datepicker-time',
24353                 cn: [
24354                 {
24355                     tag: 'table',
24356                     cls: 'table-condensed',
24357                     cn:[
24358                         {
24359                             tag: 'tbody',
24360                             cn: [
24361                                 {
24362                                     tag: 'tr',
24363                                     cn: [
24364                                     {
24365                                         tag: 'td',
24366                                         colspan: '7'
24367                                     }
24368                                     ]
24369                                 }
24370                             ]
24371                         },
24372                         {
24373                             tag: 'tfoot',
24374                             cn: [
24375                                 {
24376                                     tag: 'tr',
24377                                     cn: [
24378                                     {
24379                                         tag: 'th',
24380                                         colspan: '7',
24381                                         cls: '',
24382                                         cn: [
24383                                             {
24384                                                 tag: 'button',
24385                                                 cls: 'btn btn-info ok',
24386                                                 html: 'OK'
24387                                             }
24388                                         ]
24389                                     }
24390                     
24391                                     ]
24392                                 }
24393                             ]
24394                         }
24395                     ]
24396                 }
24397                 ]
24398             }
24399         ]
24400     }
24401 });
24402
24403  
24404
24405  /*
24406  * - LGPL
24407  *
24408  * MonthField
24409  * 
24410  */
24411
24412 /**
24413  * @class Roo.bootstrap.form.MonthField
24414  * @extends Roo.bootstrap.form.Input
24415  * Bootstrap MonthField class
24416  * 
24417  * @cfg {String} language default en
24418  * 
24419  * @constructor
24420  * Create a new MonthField
24421  * @param {Object} config The config object
24422  */
24423
24424 Roo.bootstrap.form.MonthField = function(config){
24425     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24426     
24427     this.addEvents({
24428         /**
24429          * @event show
24430          * Fires when this field show.
24431          * @param {Roo.bootstrap.form.MonthField} this
24432          * @param {Mixed} date The date value
24433          */
24434         show : true,
24435         /**
24436          * @event show
24437          * Fires when this field hide.
24438          * @param {Roo.bootstrap.form.MonthField} this
24439          * @param {Mixed} date The date value
24440          */
24441         hide : true,
24442         /**
24443          * @event select
24444          * Fires when select a date.
24445          * @param {Roo.bootstrap.form.MonthField} this
24446          * @param {String} oldvalue The old value
24447          * @param {String} newvalue The new value
24448          */
24449         select : true
24450     });
24451 };
24452
24453 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24454     
24455     onRender: function(ct, position)
24456     {
24457         
24458         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24459         
24460         this.language = this.language || 'en';
24461         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24462         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24463         
24464         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24465         this.isInline = false;
24466         this.isInput = true;
24467         this.component = this.el.select('.add-on', true).first() || false;
24468         this.component = (this.component && this.component.length === 0) ? false : this.component;
24469         this.hasInput = this.component && this.inputEL().length;
24470         
24471         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24472         
24473         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24474         
24475         this.picker().on('mousedown', this.onMousedown, this);
24476         this.picker().on('click', this.onClick, this);
24477         
24478         this.picker().addClass('datepicker-dropdown');
24479         
24480         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24481             v.setStyle('width', '189px');
24482         });
24483         
24484         this.fillMonths();
24485         
24486         this.update();
24487         
24488         if(this.isInline) {
24489             this.show();
24490         }
24491         
24492     },
24493     
24494     setValue: function(v, suppressEvent)
24495     {   
24496         var o = this.getValue();
24497         
24498         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24499         
24500         this.update();
24501
24502         if(suppressEvent !== true){
24503             this.fireEvent('select', this, o, v);
24504         }
24505         
24506     },
24507     
24508     getValue: function()
24509     {
24510         return this.value;
24511     },
24512     
24513     onClick: function(e) 
24514     {
24515         e.stopPropagation();
24516         e.preventDefault();
24517         
24518         var target = e.getTarget();
24519         
24520         if(target.nodeName.toLowerCase() === 'i'){
24521             target = Roo.get(target).dom.parentNode;
24522         }
24523         
24524         var nodeName = target.nodeName;
24525         var className = target.className;
24526         var html = target.innerHTML;
24527         
24528         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24529             return;
24530         }
24531         
24532         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24533         
24534         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24535         
24536         this.hide();
24537                         
24538     },
24539     
24540     picker : function()
24541     {
24542         return this.pickerEl;
24543     },
24544     
24545     fillMonths: function()
24546     {    
24547         var i = 0;
24548         var months = this.picker().select('>.datepicker-months td', true).first();
24549         
24550         months.dom.innerHTML = '';
24551         
24552         while (i < 12) {
24553             var month = {
24554                 tag: 'span',
24555                 cls: 'month',
24556                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24557             };
24558             
24559             months.createChild(month);
24560         }
24561         
24562     },
24563     
24564     update: function()
24565     {
24566         var _this = this;
24567         
24568         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24569             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24570         }
24571         
24572         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24573             e.removeClass('active');
24574             
24575             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24576                 e.addClass('active');
24577             }
24578         })
24579     },
24580     
24581     place: function()
24582     {
24583         if(this.isInline) {
24584             return;
24585         }
24586         
24587         this.picker().removeClass(['bottom', 'top']);
24588         
24589         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24590             /*
24591              * place to the top of element!
24592              *
24593              */
24594             
24595             this.picker().addClass('top');
24596             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24597             
24598             return;
24599         }
24600         
24601         this.picker().addClass('bottom');
24602         
24603         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24604     },
24605     
24606     onFocus : function()
24607     {
24608         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24609         this.show();
24610     },
24611     
24612     onBlur : function()
24613     {
24614         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24615         
24616         var d = this.inputEl().getValue();
24617         
24618         this.setValue(d);
24619                 
24620         this.hide();
24621     },
24622     
24623     show : function()
24624     {
24625         this.picker().show();
24626         this.picker().select('>.datepicker-months', true).first().show();
24627         this.update();
24628         this.place();
24629         
24630         this.fireEvent('show', this, this.date);
24631     },
24632     
24633     hide : function()
24634     {
24635         if(this.isInline) {
24636             return;
24637         }
24638         this.picker().hide();
24639         this.fireEvent('hide', this, this.date);
24640         
24641     },
24642     
24643     onMousedown: function(e)
24644     {
24645         e.stopPropagation();
24646         e.preventDefault();
24647     },
24648     
24649     keyup: function(e)
24650     {
24651         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24652         this.update();
24653     },
24654
24655     fireKey: function(e)
24656     {
24657         if (!this.picker().isVisible()){
24658             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24659                 this.show();
24660             }
24661             return;
24662         }
24663         
24664         var dir;
24665         
24666         switch(e.keyCode){
24667             case 27: // escape
24668                 this.hide();
24669                 e.preventDefault();
24670                 break;
24671             case 37: // left
24672             case 39: // right
24673                 dir = e.keyCode == 37 ? -1 : 1;
24674                 
24675                 this.vIndex = this.vIndex + dir;
24676                 
24677                 if(this.vIndex < 0){
24678                     this.vIndex = 0;
24679                 }
24680                 
24681                 if(this.vIndex > 11){
24682                     this.vIndex = 11;
24683                 }
24684                 
24685                 if(isNaN(this.vIndex)){
24686                     this.vIndex = 0;
24687                 }
24688                 
24689                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24690                 
24691                 break;
24692             case 38: // up
24693             case 40: // down
24694                 
24695                 dir = e.keyCode == 38 ? -1 : 1;
24696                 
24697                 this.vIndex = this.vIndex + dir * 4;
24698                 
24699                 if(this.vIndex < 0){
24700                     this.vIndex = 0;
24701                 }
24702                 
24703                 if(this.vIndex > 11){
24704                     this.vIndex = 11;
24705                 }
24706                 
24707                 if(isNaN(this.vIndex)){
24708                     this.vIndex = 0;
24709                 }
24710                 
24711                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24712                 break;
24713                 
24714             case 13: // enter
24715                 
24716                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24717                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24718                 }
24719                 
24720                 this.hide();
24721                 e.preventDefault();
24722                 break;
24723             case 9: // tab
24724                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24725                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24726                 }
24727                 this.hide();
24728                 break;
24729             case 16: // shift
24730             case 17: // ctrl
24731             case 18: // alt
24732                 break;
24733             default :
24734                 this.hide();
24735                 
24736         }
24737     },
24738     
24739     remove: function() 
24740     {
24741         this.picker().remove();
24742     }
24743    
24744 });
24745
24746 Roo.apply(Roo.bootstrap.form.MonthField,  {
24747     
24748     content : {
24749         tag: 'tbody',
24750         cn: [
24751         {
24752             tag: 'tr',
24753             cn: [
24754             {
24755                 tag: 'td',
24756                 colspan: '7'
24757             }
24758             ]
24759         }
24760         ]
24761     },
24762     
24763     dates:{
24764         en: {
24765             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24766             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24767         }
24768     }
24769 });
24770
24771 Roo.apply(Roo.bootstrap.form.MonthField,  {
24772   
24773     template : {
24774         tag: 'div',
24775         cls: 'datepicker dropdown-menu roo-dynamic',
24776         cn: [
24777             {
24778                 tag: 'div',
24779                 cls: 'datepicker-months',
24780                 cn: [
24781                 {
24782                     tag: 'table',
24783                     cls: 'table-condensed',
24784                     cn:[
24785                         Roo.bootstrap.form.DateField.content
24786                     ]
24787                 }
24788                 ]
24789             }
24790         ]
24791     }
24792 });
24793
24794  
24795
24796  
24797  /*
24798  * - LGPL
24799  *
24800  * CheckBox
24801  * 
24802  */
24803
24804 /**
24805  * @class Roo.bootstrap.form.CheckBox
24806  * @extends Roo.bootstrap.form.Input
24807  * Bootstrap CheckBox class
24808  * 
24809  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24810  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24811  * @cfg {String} boxLabel The text that appears beside the checkbox
24812  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24813  * @cfg {Boolean} checked initnal the element
24814  * @cfg {Boolean} inline inline the element (default false)
24815  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24816  * @cfg {String} tooltip label tooltip
24817  * 
24818  * @constructor
24819  * Create a new CheckBox
24820  * @param {Object} config The config object
24821  */
24822
24823 Roo.bootstrap.form.CheckBox = function(config){
24824     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24825    
24826     this.addEvents({
24827         /**
24828         * @event check
24829         * Fires when the element is checked or unchecked.
24830         * @param {Roo.bootstrap.form.CheckBox} this This input
24831         * @param {Boolean} checked The new checked value
24832         */
24833        check : true,
24834        /**
24835         * @event click
24836         * Fires when the element is click.
24837         * @param {Roo.bootstrap.form.CheckBox} this This input
24838         */
24839        click : true
24840     });
24841     
24842 };
24843
24844 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24845   
24846     inputType: 'checkbox',
24847     inputValue: 1,
24848     valueOff: 0,
24849     boxLabel: false,
24850     checked: false,
24851     weight : false,
24852     inline: false,
24853     tooltip : '',
24854     
24855     // checkbox success does not make any sense really.. 
24856     invalidClass : "",
24857     validClass : "",
24858     
24859     
24860     getAutoCreate : function()
24861     {
24862         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24863         
24864         var id = Roo.id();
24865         
24866         var cfg = {};
24867         
24868         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24869         
24870         if(this.inline){
24871             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24872         }
24873         
24874         var input =  {
24875             tag: 'input',
24876             id : id,
24877             type : this.inputType,
24878             value : this.inputValue,
24879             cls : 'roo-' + this.inputType, //'form-box',
24880             placeholder : this.placeholder || ''
24881             
24882         };
24883         
24884         if(this.inputType != 'radio'){
24885             var hidden =  {
24886                 tag: 'input',
24887                 type : 'hidden',
24888                 cls : 'roo-hidden-value',
24889                 value : this.checked ? this.inputValue : this.valueOff
24890             };
24891         }
24892         
24893             
24894         if (this.weight) { // Validity check?
24895             cfg.cls += " " + this.inputType + "-" + this.weight;
24896         }
24897         
24898         if (this.disabled) {
24899             input.disabled=true;
24900         }
24901         
24902         if(this.checked){
24903             input.checked = this.checked;
24904         }
24905         
24906         if (this.name) {
24907             
24908             input.name = this.name;
24909             
24910             if(this.inputType != 'radio'){
24911                 hidden.name = this.name;
24912                 input.name = '_hidden_' + this.name;
24913             }
24914         }
24915         
24916         if (this.size) {
24917             input.cls += ' input-' + this.size;
24918         }
24919         
24920         var settings=this;
24921         
24922         ['xs','sm','md','lg'].map(function(size){
24923             if (settings[size]) {
24924                 cfg.cls += ' col-' + size + '-' + settings[size];
24925             }
24926         });
24927         
24928         var inputblock = input;
24929          
24930         if (this.before || this.after) {
24931             
24932             inputblock = {
24933                 cls : 'input-group',
24934                 cn :  [] 
24935             };
24936             
24937             if (this.before) {
24938                 inputblock.cn.push({
24939                     tag :'span',
24940                     cls : 'input-group-addon',
24941                     html : this.before
24942                 });
24943             }
24944             
24945             inputblock.cn.push(input);
24946             
24947             if(this.inputType != 'radio'){
24948                 inputblock.cn.push(hidden);
24949             }
24950             
24951             if (this.after) {
24952                 inputblock.cn.push({
24953                     tag :'span',
24954                     cls : 'input-group-addon',
24955                     html : this.after
24956                 });
24957             }
24958             
24959         }
24960         var boxLabelCfg = false;
24961         
24962         if(this.boxLabel){
24963            
24964             boxLabelCfg = {
24965                 tag: 'label',
24966                 //'for': id, // box label is handled by onclick - so no for...
24967                 cls: 'box-label',
24968                 html: this.boxLabel
24969             };
24970             if(this.tooltip){
24971                 boxLabelCfg.tooltip = this.tooltip;
24972             }
24973              
24974         }
24975         
24976         
24977         if (align ==='left' && this.fieldLabel.length) {
24978 //                Roo.log("left and has label");
24979             cfg.cn = [
24980                 {
24981                     tag: 'label',
24982                     'for' :  id,
24983                     cls : 'control-label',
24984                     html : this.fieldLabel
24985                 },
24986                 {
24987                     cls : "", 
24988                     cn: [
24989                         inputblock
24990                     ]
24991                 }
24992             ];
24993             
24994             if (boxLabelCfg) {
24995                 cfg.cn[1].cn.push(boxLabelCfg);
24996             }
24997             
24998             if(this.labelWidth > 12){
24999                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25000             }
25001             
25002             if(this.labelWidth < 13 && this.labelmd == 0){
25003                 this.labelmd = this.labelWidth;
25004             }
25005             
25006             if(this.labellg > 0){
25007                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25008                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25009             }
25010             
25011             if(this.labelmd > 0){
25012                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25013                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25014             }
25015             
25016             if(this.labelsm > 0){
25017                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25018                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25019             }
25020             
25021             if(this.labelxs > 0){
25022                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25023                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25024             }
25025             
25026         } else if ( this.fieldLabel.length) {
25027 //                Roo.log(" label");
25028                 cfg.cn = [
25029                    
25030                     {
25031                         tag: this.boxLabel ? 'span' : 'label',
25032                         'for': id,
25033                         cls: 'control-label box-input-label',
25034                         //cls : 'input-group-addon',
25035                         html : this.fieldLabel
25036                     },
25037                     
25038                     inputblock
25039                     
25040                 ];
25041                 if (boxLabelCfg) {
25042                     cfg.cn.push(boxLabelCfg);
25043                 }
25044
25045         } else {
25046             
25047 //                Roo.log(" no label && no align");
25048                 cfg.cn = [  inputblock ] ;
25049                 if (boxLabelCfg) {
25050                     cfg.cn.push(boxLabelCfg);
25051                 }
25052
25053                 
25054         }
25055         
25056        
25057         
25058         if(this.inputType != 'radio'){
25059             cfg.cn.push(hidden);
25060         }
25061         
25062         return cfg;
25063         
25064     },
25065     
25066     /**
25067      * return the real input element.
25068      */
25069     inputEl: function ()
25070     {
25071         return this.el.select('input.roo-' + this.inputType,true).first();
25072     },
25073     hiddenEl: function ()
25074     {
25075         return this.el.select('input.roo-hidden-value',true).first();
25076     },
25077     
25078     labelEl: function()
25079     {
25080         return this.el.select('label.control-label',true).first();
25081     },
25082     /* depricated... */
25083     
25084     label: function()
25085     {
25086         return this.labelEl();
25087     },
25088     
25089     boxLabelEl: function()
25090     {
25091         return this.el.select('label.box-label',true).first();
25092     },
25093     
25094     initEvents : function()
25095     {
25096 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25097         
25098         this.inputEl().on('click', this.onClick,  this);
25099         
25100         if (this.boxLabel) { 
25101             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25102         }
25103         
25104         this.startValue = this.getValue();
25105         
25106         if(this.groupId){
25107             Roo.bootstrap.form.CheckBox.register(this);
25108         }
25109     },
25110     
25111     onClick : function(e)
25112     {   
25113         if(this.fireEvent('click', this, e) !== false){
25114             this.setChecked(!this.checked);
25115         }
25116         
25117     },
25118     
25119     setChecked : function(state,suppressEvent)
25120     {
25121         this.startValue = this.getValue();
25122
25123         if(this.inputType == 'radio'){
25124             
25125             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25126                 e.dom.checked = false;
25127             });
25128             
25129             this.inputEl().dom.checked = true;
25130             
25131             this.inputEl().dom.value = this.inputValue;
25132             
25133             if(suppressEvent !== true){
25134                 this.fireEvent('check', this, true);
25135             }
25136             
25137             this.validate();
25138             
25139             return;
25140         }
25141         
25142         this.checked = state;
25143         
25144         this.inputEl().dom.checked = state;
25145         
25146         
25147         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25148         
25149         if(suppressEvent !== true){
25150             this.fireEvent('check', this, state);
25151         }
25152         
25153         this.validate();
25154     },
25155     
25156     getValue : function()
25157     {
25158         if(this.inputType == 'radio'){
25159             return this.getGroupValue();
25160         }
25161         
25162         return this.hiddenEl().dom.value;
25163         
25164     },
25165     
25166     getGroupValue : function()
25167     {
25168         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25169             return '';
25170         }
25171         
25172         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25173     },
25174     
25175     setValue : function(v,suppressEvent)
25176     {
25177         if(this.inputType == 'radio'){
25178             this.setGroupValue(v, suppressEvent);
25179             return;
25180         }
25181         
25182         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25183         
25184         this.validate();
25185     },
25186     
25187     setGroupValue : function(v, suppressEvent)
25188     {
25189         this.startValue = this.getValue();
25190         
25191         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25192             e.dom.checked = false;
25193             
25194             if(e.dom.value == v){
25195                 e.dom.checked = true;
25196             }
25197         });
25198         
25199         if(suppressEvent !== true){
25200             this.fireEvent('check', this, true);
25201         }
25202
25203         this.validate();
25204         
25205         return;
25206     },
25207     
25208     validate : function()
25209     {
25210         if(this.getVisibilityEl().hasClass('hidden')){
25211             return true;
25212         }
25213         
25214         if(
25215                 this.disabled || 
25216                 (this.inputType == 'radio' && this.validateRadio()) ||
25217                 (this.inputType == 'checkbox' && this.validateCheckbox())
25218         ){
25219             this.markValid();
25220             return true;
25221         }
25222         
25223         this.markInvalid();
25224         return false;
25225     },
25226     
25227     validateRadio : function()
25228     {
25229         if(this.getVisibilityEl().hasClass('hidden')){
25230             return true;
25231         }
25232         
25233         if(this.allowBlank){
25234             return true;
25235         }
25236         
25237         var valid = false;
25238         
25239         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25240             if(!e.dom.checked){
25241                 return;
25242             }
25243             
25244             valid = true;
25245             
25246             return false;
25247         });
25248         
25249         return valid;
25250     },
25251     
25252     validateCheckbox : function()
25253     {
25254         if(!this.groupId){
25255             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25256             //return (this.getValue() == this.inputValue) ? true : false;
25257         }
25258         
25259         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25260         
25261         if(!group){
25262             return false;
25263         }
25264         
25265         var r = false;
25266         
25267         for(var i in group){
25268             if(group[i].el.isVisible(true)){
25269                 r = false;
25270                 break;
25271             }
25272             
25273             r = true;
25274         }
25275         
25276         for(var i in group){
25277             if(r){
25278                 break;
25279             }
25280             
25281             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25282         }
25283         
25284         return r;
25285     },
25286     
25287     /**
25288      * Mark this field as valid
25289      */
25290     markValid : function()
25291     {
25292         var _this = this;
25293         
25294         this.fireEvent('valid', this);
25295         
25296         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25297         
25298         if(this.groupId){
25299             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25300         }
25301         
25302         if(label){
25303             label.markValid();
25304         }
25305
25306         if(this.inputType == 'radio'){
25307             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25308                 var fg = e.findParent('.form-group', false, true);
25309                 if (Roo.bootstrap.version == 3) {
25310                     fg.removeClass([_this.invalidClass, _this.validClass]);
25311                     fg.addClass(_this.validClass);
25312                 } else {
25313                     fg.removeClass(['is-valid', 'is-invalid']);
25314                     fg.addClass('is-valid');
25315                 }
25316             });
25317             
25318             return;
25319         }
25320
25321         if(!this.groupId){
25322             var fg = this.el.findParent('.form-group', false, true);
25323             if (Roo.bootstrap.version == 3) {
25324                 fg.removeClass([this.invalidClass, this.validClass]);
25325                 fg.addClass(this.validClass);
25326             } else {
25327                 fg.removeClass(['is-valid', 'is-invalid']);
25328                 fg.addClass('is-valid');
25329             }
25330             return;
25331         }
25332         
25333         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25334         
25335         if(!group){
25336             return;
25337         }
25338         
25339         for(var i in group){
25340             var fg = group[i].el.findParent('.form-group', false, true);
25341             if (Roo.bootstrap.version == 3) {
25342                 fg.removeClass([this.invalidClass, this.validClass]);
25343                 fg.addClass(this.validClass);
25344             } else {
25345                 fg.removeClass(['is-valid', 'is-invalid']);
25346                 fg.addClass('is-valid');
25347             }
25348         }
25349     },
25350     
25351      /**
25352      * Mark this field as invalid
25353      * @param {String} msg The validation message
25354      */
25355     markInvalid : function(msg)
25356     {
25357         if(this.allowBlank){
25358             return;
25359         }
25360         
25361         var _this = this;
25362         
25363         this.fireEvent('invalid', this, msg);
25364         
25365         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25366         
25367         if(this.groupId){
25368             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25369         }
25370         
25371         if(label){
25372             label.markInvalid();
25373         }
25374             
25375         if(this.inputType == 'radio'){
25376             
25377             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25378                 var fg = e.findParent('.form-group', false, true);
25379                 if (Roo.bootstrap.version == 3) {
25380                     fg.removeClass([_this.invalidClass, _this.validClass]);
25381                     fg.addClass(_this.invalidClass);
25382                 } else {
25383                     fg.removeClass(['is-invalid', 'is-valid']);
25384                     fg.addClass('is-invalid');
25385                 }
25386             });
25387             
25388             return;
25389         }
25390         
25391         if(!this.groupId){
25392             var fg = this.el.findParent('.form-group', false, true);
25393             if (Roo.bootstrap.version == 3) {
25394                 fg.removeClass([_this.invalidClass, _this.validClass]);
25395                 fg.addClass(_this.invalidClass);
25396             } else {
25397                 fg.removeClass(['is-invalid', 'is-valid']);
25398                 fg.addClass('is-invalid');
25399             }
25400             return;
25401         }
25402         
25403         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25404         
25405         if(!group){
25406             return;
25407         }
25408         
25409         for(var i in group){
25410             var fg = group[i].el.findParent('.form-group', false, true);
25411             if (Roo.bootstrap.version == 3) {
25412                 fg.removeClass([_this.invalidClass, _this.validClass]);
25413                 fg.addClass(_this.invalidClass);
25414             } else {
25415                 fg.removeClass(['is-invalid', 'is-valid']);
25416                 fg.addClass('is-invalid');
25417             }
25418         }
25419         
25420     },
25421     
25422     clearInvalid : function()
25423     {
25424         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25425         
25426         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25427         
25428         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25429         
25430         if (label && label.iconEl) {
25431             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25432             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25433         }
25434     },
25435     
25436     disable : function()
25437     {
25438         if(this.inputType != 'radio'){
25439             Roo.bootstrap.form.CheckBox.superclass.disable.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().addClass(this.disabledClass);
25448                 e.dom.disabled = true;
25449             });
25450         }
25451         
25452         this.disabled = true;
25453         this.fireEvent("disable", this);
25454         return this;
25455     },
25456
25457     enable : function()
25458     {
25459         if(this.inputType != 'radio'){
25460             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25461             return;
25462         }
25463         
25464         var _this = this;
25465         
25466         if(this.rendered){
25467             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25468                 _this.getActionEl().removeClass(this.disabledClass);
25469                 e.dom.disabled = false;
25470             });
25471         }
25472         
25473         this.disabled = false;
25474         this.fireEvent("enable", this);
25475         return this;
25476     },
25477     
25478     setBoxLabel : function(v)
25479     {
25480         this.boxLabel = v;
25481         
25482         if(this.rendered){
25483             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25484         }
25485     }
25486
25487 });
25488
25489 Roo.apply(Roo.bootstrap.form.CheckBox, {
25490     
25491     groups: {},
25492     
25493      /**
25494     * register a CheckBox Group
25495     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25496     */
25497     register : function(checkbox)
25498     {
25499         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25500             this.groups[checkbox.groupId] = {};
25501         }
25502         
25503         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25504             return;
25505         }
25506         
25507         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25508         
25509     },
25510     /**
25511     * fetch a CheckBox Group based on the group ID
25512     * @param {string} the group ID
25513     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25514     */
25515     get: function(groupId) {
25516         if (typeof(this.groups[groupId]) == 'undefined') {
25517             return false;
25518         }
25519         
25520         return this.groups[groupId] ;
25521     }
25522     
25523     
25524 });
25525 /*
25526  * - LGPL
25527  *
25528  * RadioItem
25529  * 
25530  */
25531
25532 /**
25533  * @class Roo.bootstrap.form.Radio
25534  * @extends Roo.bootstrap.Component
25535  * Bootstrap Radio class
25536  * @cfg {String} boxLabel - the label associated
25537  * @cfg {String} value - the value of radio
25538  * 
25539  * @constructor
25540  * Create a new Radio
25541  * @param {Object} config The config object
25542  */
25543 Roo.bootstrap.form.Radio = function(config){
25544     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25545     
25546 };
25547
25548 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25549     
25550     boxLabel : '',
25551     
25552     value : '',
25553     
25554     getAutoCreate : function()
25555     {
25556         var cfg = {
25557             tag : 'div',
25558             cls : 'form-group radio',
25559             cn : [
25560                 {
25561                     tag : 'label',
25562                     cls : 'box-label',
25563                     html : this.boxLabel
25564                 }
25565             ]
25566         };
25567         
25568         return cfg;
25569     },
25570     
25571     initEvents : function() 
25572     {
25573         this.parent().register(this);
25574         
25575         this.el.on('click', this.onClick, this);
25576         
25577     },
25578     
25579     onClick : function(e)
25580     {
25581         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25582             this.setChecked(true);
25583         }
25584     },
25585     
25586     setChecked : function(state, suppressEvent)
25587     {
25588         this.parent().setValue(this.value, suppressEvent);
25589         
25590     },
25591     
25592     setBoxLabel : function(v)
25593     {
25594         this.boxLabel = v;
25595         
25596         if(this.rendered){
25597             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25598         }
25599     }
25600     
25601 });
25602  
25603
25604  /*
25605  * - LGPL
25606  *
25607  * Input
25608  * 
25609  */
25610
25611 /**
25612  * @class Roo.bootstrap.form.SecurePass
25613  * @extends Roo.bootstrap.form.Input
25614  * Bootstrap SecurePass class
25615  *
25616  * 
25617  * @constructor
25618  * Create a new SecurePass
25619  * @param {Object} config The config object
25620  */
25621  
25622 Roo.bootstrap.form.SecurePass = function (config) {
25623     // these go here, so the translation tool can replace them..
25624     this.errors = {
25625         PwdEmpty: "Please type a password, and then retype it to confirm.",
25626         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25627         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25628         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25629         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25630         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25631         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25632         TooWeak: "Your password is Too Weak."
25633     },
25634     this.meterLabel = "Password strength:";
25635     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25636     this.meterClass = [
25637         "roo-password-meter-tooweak", 
25638         "roo-password-meter-weak", 
25639         "roo-password-meter-medium", 
25640         "roo-password-meter-strong", 
25641         "roo-password-meter-grey"
25642     ];
25643     
25644     this.errors = {};
25645     
25646     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25647 }
25648
25649 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25650     /**
25651      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25652      * {
25653      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25654      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25655      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25656      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25657      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25658      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25659      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25660      * })
25661      */
25662     // private
25663     
25664     meterWidth: 300,
25665     errorMsg :'',    
25666     errors: false,
25667     imageRoot: '/',
25668     /**
25669      * @cfg {String/Object} Label for the strength meter (defaults to
25670      * 'Password strength:')
25671      */
25672     // private
25673     meterLabel: '',
25674     /**
25675      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25676      * ['Weak', 'Medium', 'Strong'])
25677      */
25678     // private    
25679     pwdStrengths: false,    
25680     // private
25681     strength: 0,
25682     // private
25683     _lastPwd: null,
25684     // private
25685     kCapitalLetter: 0,
25686     kSmallLetter: 1,
25687     kDigit: 2,
25688     kPunctuation: 3,
25689     
25690     insecure: false,
25691     // private
25692     initEvents: function ()
25693     {
25694         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25695
25696         if (this.el.is('input[type=password]') && Roo.isSafari) {
25697             this.el.on('keydown', this.SafariOnKeyDown, this);
25698         }
25699
25700         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25701     },
25702     // private
25703     onRender: function (ct, position)
25704     {
25705         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25706         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25707         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25708
25709         this.trigger.createChild({
25710                    cn: [
25711                     {
25712                     //id: 'PwdMeter',
25713                     tag: 'div',
25714                     cls: 'roo-password-meter-grey col-xs-12',
25715                     style: {
25716                         //width: 0,
25717                         //width: this.meterWidth + 'px'                                                
25718                         }
25719                     },
25720                     {                            
25721                          cls: 'roo-password-meter-text'                          
25722                     }
25723                 ]            
25724         });
25725
25726          
25727         if (this.hideTrigger) {
25728             this.trigger.setDisplayed(false);
25729         }
25730         this.setSize(this.width || '', this.height || '');
25731     },
25732     // private
25733     onDestroy: function ()
25734     {
25735         if (this.trigger) {
25736             this.trigger.removeAllListeners();
25737             this.trigger.remove();
25738         }
25739         if (this.wrap) {
25740             this.wrap.remove();
25741         }
25742         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25743     },
25744     // private
25745     checkStrength: function ()
25746     {
25747         var pwd = this.inputEl().getValue();
25748         if (pwd == this._lastPwd) {
25749             return;
25750         }
25751
25752         var strength;
25753         if (this.ClientSideStrongPassword(pwd)) {
25754             strength = 3;
25755         } else if (this.ClientSideMediumPassword(pwd)) {
25756             strength = 2;
25757         } else if (this.ClientSideWeakPassword(pwd)) {
25758             strength = 1;
25759         } else {
25760             strength = 0;
25761         }
25762         
25763         Roo.log('strength1: ' + strength);
25764         
25765         //var pm = this.trigger.child('div/div/div').dom;
25766         var pm = this.trigger.child('div/div');
25767         pm.removeClass(this.meterClass);
25768         pm.addClass(this.meterClass[strength]);
25769                 
25770         
25771         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25772                 
25773         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25774         
25775         this._lastPwd = pwd;
25776     },
25777     reset: function ()
25778     {
25779         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25780         
25781         this._lastPwd = '';
25782         
25783         var pm = this.trigger.child('div/div');
25784         pm.removeClass(this.meterClass);
25785         pm.addClass('roo-password-meter-grey');        
25786         
25787         
25788         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25789         
25790         pt.innerHTML = '';
25791         this.inputEl().dom.type='password';
25792     },
25793     // private
25794     validateValue: function (value)
25795     {
25796         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25797             return false;
25798         }
25799         if (value.length == 0) {
25800             if (this.allowBlank) {
25801                 this.clearInvalid();
25802                 return true;
25803             }
25804
25805             this.markInvalid(this.errors.PwdEmpty);
25806             this.errorMsg = this.errors.PwdEmpty;
25807             return false;
25808         }
25809         
25810         if(this.insecure){
25811             return true;
25812         }
25813         
25814         if (!value.match(/[\x21-\x7e]+/)) {
25815             this.markInvalid(this.errors.PwdBadChar);
25816             this.errorMsg = this.errors.PwdBadChar;
25817             return false;
25818         }
25819         if (value.length < 6) {
25820             this.markInvalid(this.errors.PwdShort);
25821             this.errorMsg = this.errors.PwdShort;
25822             return false;
25823         }
25824         if (value.length > 16) {
25825             this.markInvalid(this.errors.PwdLong);
25826             this.errorMsg = this.errors.PwdLong;
25827             return false;
25828         }
25829         var strength;
25830         if (this.ClientSideStrongPassword(value)) {
25831             strength = 3;
25832         } else if (this.ClientSideMediumPassword(value)) {
25833             strength = 2;
25834         } else if (this.ClientSideWeakPassword(value)) {
25835             strength = 1;
25836         } else {
25837             strength = 0;
25838         }
25839
25840         
25841         if (strength < 2) {
25842             //this.markInvalid(this.errors.TooWeak);
25843             this.errorMsg = this.errors.TooWeak;
25844             //return false;
25845         }
25846         
25847         
25848         console.log('strength2: ' + strength);
25849         
25850         //var pm = this.trigger.child('div/div/div').dom;
25851         
25852         var pm = this.trigger.child('div/div');
25853         pm.removeClass(this.meterClass);
25854         pm.addClass(this.meterClass[strength]);
25855                 
25856         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25857                 
25858         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25859         
25860         this.errorMsg = ''; 
25861         return true;
25862     },
25863     // private
25864     CharacterSetChecks: function (type)
25865     {
25866         this.type = type;
25867         this.fResult = false;
25868     },
25869     // private
25870     isctype: function (character, type)
25871     {
25872         switch (type) {  
25873             case this.kCapitalLetter:
25874                 if (character >= 'A' && character <= 'Z') {
25875                     return true;
25876                 }
25877                 break;
25878             
25879             case this.kSmallLetter:
25880                 if (character >= 'a' && character <= 'z') {
25881                     return true;
25882                 }
25883                 break;
25884             
25885             case this.kDigit:
25886                 if (character >= '0' && character <= '9') {
25887                     return true;
25888                 }
25889                 break;
25890             
25891             case this.kPunctuation:
25892                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25893                     return true;
25894                 }
25895                 break;
25896             
25897             default:
25898                 return false;
25899         }
25900
25901     },
25902     // private
25903     IsLongEnough: function (pwd, size)
25904     {
25905         return !(pwd == null || isNaN(size) || pwd.length < size);
25906     },
25907     // private
25908     SpansEnoughCharacterSets: function (word, nb)
25909     {
25910         if (!this.IsLongEnough(word, nb))
25911         {
25912             return false;
25913         }
25914
25915         var characterSetChecks = new Array(
25916             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25917             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25918         );
25919         
25920         for (var index = 0; index < word.length; ++index) {
25921             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25922                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25923                     characterSetChecks[nCharSet].fResult = true;
25924                     break;
25925                 }
25926             }
25927         }
25928
25929         var nCharSets = 0;
25930         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25931             if (characterSetChecks[nCharSet].fResult) {
25932                 ++nCharSets;
25933             }
25934         }
25935
25936         if (nCharSets < nb) {
25937             return false;
25938         }
25939         return true;
25940     },
25941     // private
25942     ClientSideStrongPassword: function (pwd)
25943     {
25944         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25945     },
25946     // private
25947     ClientSideMediumPassword: function (pwd)
25948     {
25949         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25950     },
25951     // private
25952     ClientSideWeakPassword: function (pwd)
25953     {
25954         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25955     }
25956           
25957 })//<script type="text/javascript">
25958
25959 /*
25960  * Based  Ext JS Library 1.1.1
25961  * Copyright(c) 2006-2007, Ext JS, LLC.
25962  * LGPL
25963  *
25964  */
25965  
25966 /**
25967  * @class Roo.HtmlEditorCore
25968  * @extends Roo.Component
25969  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25970  *
25971  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25972  */
25973
25974 Roo.HtmlEditorCore = function(config){
25975     
25976     
25977     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25978     
25979     
25980     this.addEvents({
25981         /**
25982          * @event initialize
25983          * Fires when the editor is fully initialized (including the iframe)
25984          * @param {Roo.HtmlEditorCore} this
25985          */
25986         initialize: true,
25987         /**
25988          * @event activate
25989          * Fires when the editor is first receives the focus. Any insertion must wait
25990          * until after this event.
25991          * @param {Roo.HtmlEditorCore} this
25992          */
25993         activate: true,
25994          /**
25995          * @event beforesync
25996          * Fires before the textarea is updated with content from the editor iframe. Return false
25997          * to cancel the sync.
25998          * @param {Roo.HtmlEditorCore} this
25999          * @param {String} html
26000          */
26001         beforesync: true,
26002          /**
26003          * @event beforepush
26004          * Fires before the iframe editor is updated with content from the textarea. Return false
26005          * to cancel the push.
26006          * @param {Roo.HtmlEditorCore} this
26007          * @param {String} html
26008          */
26009         beforepush: true,
26010          /**
26011          * @event sync
26012          * Fires when the textarea is updated with content from the editor iframe.
26013          * @param {Roo.HtmlEditorCore} this
26014          * @param {String} html
26015          */
26016         sync: true,
26017          /**
26018          * @event push
26019          * Fires when the iframe editor is updated with content from the textarea.
26020          * @param {Roo.HtmlEditorCore} this
26021          * @param {String} html
26022          */
26023         push: true,
26024         
26025         /**
26026          * @event editorevent
26027          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26028          * @param {Roo.HtmlEditorCore} this
26029          */
26030         editorevent: true
26031         
26032     });
26033     
26034     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26035     
26036     // defaults : white / black...
26037     this.applyBlacklists();
26038     
26039     
26040     
26041 };
26042
26043
26044 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26045
26046
26047      /**
26048      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26049      */
26050     
26051     owner : false,
26052     
26053      /**
26054      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26055      *                        Roo.resizable.
26056      */
26057     resizable : false,
26058      /**
26059      * @cfg {Number} height (in pixels)
26060      */   
26061     height: 300,
26062    /**
26063      * @cfg {Number} width (in pixels)
26064      */   
26065     width: 500,
26066     
26067     /**
26068      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26069      * 
26070      */
26071     stylesheets: false,
26072     
26073     /**
26074      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26075      */
26076     allowComments: false,
26077     // id of frame..
26078     frameId: false,
26079     
26080     // private properties
26081     validationEvent : false,
26082     deferHeight: true,
26083     initialized : false,
26084     activated : false,
26085     sourceEditMode : false,
26086     onFocus : Roo.emptyFn,
26087     iframePad:3,
26088     hideMode:'offsets',
26089     
26090     clearUp: true,
26091     
26092     // blacklist + whitelisted elements..
26093     black: false,
26094     white: false,
26095      
26096     bodyCls : '',
26097
26098     /**
26099      * Protected method that will not generally be called directly. It
26100      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26101      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26102      */
26103     getDocMarkup : function(){
26104         // body styles..
26105         var st = '';
26106         
26107         // inherit styels from page...?? 
26108         if (this.stylesheets === false) {
26109             
26110             Roo.get(document.head).select('style').each(function(node) {
26111                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26112             });
26113             
26114             Roo.get(document.head).select('link').each(function(node) { 
26115                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26116             });
26117             
26118         } else if (!this.stylesheets.length) {
26119                 // simple..
26120                 st = '<style type="text/css">' +
26121                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26122                    '</style>';
26123         } else {
26124             for (var i in this.stylesheets) { 
26125                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26126             }
26127             
26128         }
26129         
26130         st +=  '<style type="text/css">' +
26131             'IMG { cursor: pointer } ' +
26132         '</style>';
26133
26134         var cls = 'roo-htmleditor-body';
26135         
26136         if(this.bodyCls.length){
26137             cls += ' ' + this.bodyCls;
26138         }
26139         
26140         return '<html><head>' + st  +
26141             //<style type="text/css">' +
26142             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26143             //'</style>' +
26144             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26145     },
26146
26147     // private
26148     onRender : function(ct, position)
26149     {
26150         var _t = this;
26151         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26152         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26153         
26154         
26155         this.el.dom.style.border = '0 none';
26156         this.el.dom.setAttribute('tabIndex', -1);
26157         this.el.addClass('x-hidden hide');
26158         
26159         
26160         
26161         if(Roo.isIE){ // fix IE 1px bogus margin
26162             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26163         }
26164        
26165         
26166         this.frameId = Roo.id();
26167         
26168          
26169         
26170         var iframe = this.owner.wrap.createChild({
26171             tag: 'iframe',
26172             cls: 'form-control', // bootstrap..
26173             id: this.frameId,
26174             name: this.frameId,
26175             frameBorder : 'no',
26176             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26177         }, this.el
26178         );
26179         
26180         
26181         this.iframe = iframe.dom;
26182
26183          this.assignDocWin();
26184         
26185         this.doc.designMode = 'on';
26186        
26187         this.doc.open();
26188         this.doc.write(this.getDocMarkup());
26189         this.doc.close();
26190
26191         
26192         var task = { // must defer to wait for browser to be ready
26193             run : function(){
26194                 //console.log("run task?" + this.doc.readyState);
26195                 this.assignDocWin();
26196                 if(this.doc.body || this.doc.readyState == 'complete'){
26197                     try {
26198                         this.doc.designMode="on";
26199                     } catch (e) {
26200                         return;
26201                     }
26202                     Roo.TaskMgr.stop(task);
26203                     this.initEditor.defer(10, this);
26204                 }
26205             },
26206             interval : 10,
26207             duration: 10000,
26208             scope: this
26209         };
26210         Roo.TaskMgr.start(task);
26211
26212     },
26213
26214     // private
26215     onResize : function(w, h)
26216     {
26217          Roo.log('resize: ' +w + ',' + h );
26218         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26219         if(!this.iframe){
26220             return;
26221         }
26222         if(typeof w == 'number'){
26223             
26224             this.iframe.style.width = w + 'px';
26225         }
26226         if(typeof h == 'number'){
26227             
26228             this.iframe.style.height = h + 'px';
26229             if(this.doc){
26230                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26231             }
26232         }
26233         
26234     },
26235
26236     /**
26237      * Toggles the editor between standard and source edit mode.
26238      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26239      */
26240     toggleSourceEdit : function(sourceEditMode){
26241         
26242         this.sourceEditMode = sourceEditMode === true;
26243         
26244         if(this.sourceEditMode){
26245  
26246             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26247             
26248         }else{
26249             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26250             //this.iframe.className = '';
26251             this.deferFocus();
26252         }
26253         //this.setSize(this.owner.wrap.getSize());
26254         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26255     },
26256
26257     
26258   
26259
26260     /**
26261      * Protected method that will not generally be called directly. If you need/want
26262      * custom HTML cleanup, this is the method you should override.
26263      * @param {String} html The HTML to be cleaned
26264      * return {String} The cleaned HTML
26265      */
26266     cleanHtml : function(html){
26267         html = String(html);
26268         if(html.length > 5){
26269             if(Roo.isSafari){ // strip safari nonsense
26270                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26271             }
26272         }
26273         if(html == '&nbsp;'){
26274             html = '';
26275         }
26276         return html;
26277     },
26278
26279     /**
26280      * HTML Editor -> Textarea
26281      * Protected method that will not generally be called directly. Syncs the contents
26282      * of the editor iframe with the textarea.
26283      */
26284     syncValue : function(){
26285         if(this.initialized){
26286             var bd = (this.doc.body || this.doc.documentElement);
26287             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26288             var html = bd.innerHTML;
26289             if(Roo.isSafari){
26290                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26291                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26292                 if(m && m[1]){
26293                     html = '<div style="'+m[0]+'">' + html + '</div>';
26294                 }
26295             }
26296             html = this.cleanHtml(html);
26297             // fix up the special chars.. normaly like back quotes in word...
26298             // however we do not want to do this with chinese..
26299             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26300                 
26301                 var cc = match.charCodeAt();
26302
26303                 // Get the character value, handling surrogate pairs
26304                 if (match.length == 2) {
26305                     // It's a surrogate pair, calculate the Unicode code point
26306                     var high = match.charCodeAt(0) - 0xD800;
26307                     var low  = match.charCodeAt(1) - 0xDC00;
26308                     cc = (high * 0x400) + low + 0x10000;
26309                 }  else if (
26310                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26311                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26312                     (cc >= 0xf900 && cc < 0xfb00 )
26313                 ) {
26314                         return match;
26315                 }  
26316          
26317                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26318                 return "&#" + cc + ";";
26319                 
26320                 
26321             });
26322             
26323             
26324              
26325             if(this.owner.fireEvent('beforesync', this, html) !== false){
26326                 this.el.dom.value = html;
26327                 this.owner.fireEvent('sync', this, html);
26328             }
26329         }
26330     },
26331
26332     /**
26333      * Protected method that will not generally be called directly. Pushes the value of the textarea
26334      * into the iframe editor.
26335      */
26336     pushValue : function(){
26337         if(this.initialized){
26338             var v = this.el.dom.value.trim();
26339             
26340 //            if(v.length < 1){
26341 //                v = '&#160;';
26342 //            }
26343             
26344             if(this.owner.fireEvent('beforepush', this, v) !== false){
26345                 var d = (this.doc.body || this.doc.documentElement);
26346                 d.innerHTML = v;
26347                 this.cleanUpPaste();
26348                 this.el.dom.value = d.innerHTML;
26349                 this.owner.fireEvent('push', this, v);
26350             }
26351         }
26352     },
26353
26354     // private
26355     deferFocus : function(){
26356         this.focus.defer(10, this);
26357     },
26358
26359     // doc'ed in Field
26360     focus : function(){
26361         if(this.win && !this.sourceEditMode){
26362             this.win.focus();
26363         }else{
26364             this.el.focus();
26365         }
26366     },
26367     
26368     assignDocWin: function()
26369     {
26370         var iframe = this.iframe;
26371         
26372          if(Roo.isIE){
26373             this.doc = iframe.contentWindow.document;
26374             this.win = iframe.contentWindow;
26375         } else {
26376 //            if (!Roo.get(this.frameId)) {
26377 //                return;
26378 //            }
26379 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26380 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26381             
26382             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26383                 return;
26384             }
26385             
26386             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26387             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26388         }
26389     },
26390     
26391     // private
26392     initEditor : function(){
26393         //console.log("INIT EDITOR");
26394         this.assignDocWin();
26395         
26396         
26397         
26398         this.doc.designMode="on";
26399         this.doc.open();
26400         this.doc.write(this.getDocMarkup());
26401         this.doc.close();
26402         
26403         var dbody = (this.doc.body || this.doc.documentElement);
26404         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26405         // this copies styles from the containing element into thsi one..
26406         // not sure why we need all of this..
26407         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26408         
26409         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26410         //ss['background-attachment'] = 'fixed'; // w3c
26411         dbody.bgProperties = 'fixed'; // ie
26412         //Roo.DomHelper.applyStyles(dbody, ss);
26413         Roo.EventManager.on(this.doc, {
26414             //'mousedown': this.onEditorEvent,
26415             'mouseup': this.onEditorEvent,
26416             'dblclick': this.onEditorEvent,
26417             'click': this.onEditorEvent,
26418             'keyup': this.onEditorEvent,
26419             buffer:100,
26420             scope: this
26421         });
26422         if(Roo.isGecko){
26423             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26424         }
26425         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26426             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26427         }
26428         this.initialized = true;
26429
26430         this.owner.fireEvent('initialize', this);
26431         this.pushValue();
26432     },
26433
26434     // private
26435     onDestroy : function(){
26436         
26437         
26438         
26439         if(this.rendered){
26440             
26441             //for (var i =0; i < this.toolbars.length;i++) {
26442             //    // fixme - ask toolbars for heights?
26443             //    this.toolbars[i].onDestroy();
26444            // }
26445             
26446             //this.wrap.dom.innerHTML = '';
26447             //this.wrap.remove();
26448         }
26449     },
26450
26451     // private
26452     onFirstFocus : function(){
26453         
26454         this.assignDocWin();
26455         
26456         
26457         this.activated = true;
26458          
26459     
26460         if(Roo.isGecko){ // prevent silly gecko errors
26461             this.win.focus();
26462             var s = this.win.getSelection();
26463             if(!s.focusNode || s.focusNode.nodeType != 3){
26464                 var r = s.getRangeAt(0);
26465                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26466                 r.collapse(true);
26467                 this.deferFocus();
26468             }
26469             try{
26470                 this.execCmd('useCSS', true);
26471                 this.execCmd('styleWithCSS', false);
26472             }catch(e){}
26473         }
26474         this.owner.fireEvent('activate', this);
26475     },
26476
26477     // private
26478     adjustFont: function(btn){
26479         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26480         //if(Roo.isSafari){ // safari
26481         //    adjust *= 2;
26482        // }
26483         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26484         if(Roo.isSafari){ // safari
26485             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26486             v =  (v < 10) ? 10 : v;
26487             v =  (v > 48) ? 48 : v;
26488             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26489             
26490         }
26491         
26492         
26493         v = Math.max(1, v+adjust);
26494         
26495         this.execCmd('FontSize', v  );
26496     },
26497
26498     onEditorEvent : function(e)
26499     {
26500         this.owner.fireEvent('editorevent', this, e);
26501       //  this.updateToolbar();
26502         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26503     },
26504
26505     insertTag : function(tg)
26506     {
26507         // could be a bit smarter... -> wrap the current selected tRoo..
26508         if (tg.toLowerCase() == 'span' ||
26509             tg.toLowerCase() == 'code' ||
26510             tg.toLowerCase() == 'sup' ||
26511             tg.toLowerCase() == 'sub' 
26512             ) {
26513             
26514             range = this.createRange(this.getSelection());
26515             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26516             wrappingNode.appendChild(range.extractContents());
26517             range.insertNode(wrappingNode);
26518
26519             return;
26520             
26521             
26522             
26523         }
26524         this.execCmd("formatblock",   tg);
26525         
26526     },
26527     
26528     insertText : function(txt)
26529     {
26530         
26531         
26532         var range = this.createRange();
26533         range.deleteContents();
26534                //alert(Sender.getAttribute('label'));
26535                
26536         range.insertNode(this.doc.createTextNode(txt));
26537     } ,
26538     
26539      
26540
26541     /**
26542      * Executes a Midas editor command on the editor document and performs necessary focus and
26543      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26544      * @param {String} cmd The Midas command
26545      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26546      */
26547     relayCmd : function(cmd, value){
26548         this.win.focus();
26549         this.execCmd(cmd, value);
26550         this.owner.fireEvent('editorevent', this);
26551         //this.updateToolbar();
26552         this.owner.deferFocus();
26553     },
26554
26555     /**
26556      * Executes a Midas editor command directly on the editor document.
26557      * For visual commands, you should use {@link #relayCmd} instead.
26558      * <b>This should only be called after the editor is initialized.</b>
26559      * @param {String} cmd The Midas command
26560      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26561      */
26562     execCmd : function(cmd, value){
26563         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26564         this.syncValue();
26565     },
26566  
26567  
26568    
26569     /**
26570      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26571      * to insert tRoo.
26572      * @param {String} text | dom node.. 
26573      */
26574     insertAtCursor : function(text)
26575     {
26576         
26577         if(!this.activated){
26578             return;
26579         }
26580         /*
26581         if(Roo.isIE){
26582             this.win.focus();
26583             var r = this.doc.selection.createRange();
26584             if(r){
26585                 r.collapse(true);
26586                 r.pasteHTML(text);
26587                 this.syncValue();
26588                 this.deferFocus();
26589             
26590             }
26591             return;
26592         }
26593         */
26594         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26595             this.win.focus();
26596             
26597             
26598             // from jquery ui (MIT licenced)
26599             var range, node;
26600             var win = this.win;
26601             
26602             if (win.getSelection && win.getSelection().getRangeAt) {
26603                 range = win.getSelection().getRangeAt(0);
26604                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26605                 range.insertNode(node);
26606             } else if (win.document.selection && win.document.selection.createRange) {
26607                 // no firefox support
26608                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26609                 win.document.selection.createRange().pasteHTML(txt);
26610             } else {
26611                 // no firefox support
26612                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26613                 this.execCmd('InsertHTML', txt);
26614             } 
26615             
26616             this.syncValue();
26617             
26618             this.deferFocus();
26619         }
26620     },
26621  // private
26622     mozKeyPress : function(e){
26623         if(e.ctrlKey){
26624             var c = e.getCharCode(), cmd;
26625           
26626             if(c > 0){
26627                 c = String.fromCharCode(c).toLowerCase();
26628                 switch(c){
26629                     case 'b':
26630                         cmd = 'bold';
26631                         break;
26632                     case 'i':
26633                         cmd = 'italic';
26634                         break;
26635                     
26636                     case 'u':
26637                         cmd = 'underline';
26638                         break;
26639                     
26640                     case 'v':
26641                         this.cleanUpPaste.defer(100, this);
26642                         return;
26643                         
26644                 }
26645                 if(cmd){
26646                     this.win.focus();
26647                     this.execCmd(cmd);
26648                     this.deferFocus();
26649                     e.preventDefault();
26650                 }
26651                 
26652             }
26653         }
26654     },
26655
26656     // private
26657     fixKeys : function(){ // load time branching for fastest keydown performance
26658         if(Roo.isIE){
26659             return function(e){
26660                 var k = e.getKey(), r;
26661                 if(k == e.TAB){
26662                     e.stopEvent();
26663                     r = this.doc.selection.createRange();
26664                     if(r){
26665                         r.collapse(true);
26666                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26667                         this.deferFocus();
26668                     }
26669                     return;
26670                 }
26671                 
26672                 if(k == e.ENTER){
26673                     r = this.doc.selection.createRange();
26674                     if(r){
26675                         var target = r.parentElement();
26676                         if(!target || target.tagName.toLowerCase() != 'li'){
26677                             e.stopEvent();
26678                             r.pasteHTML('<br />');
26679                             r.collapse(false);
26680                             r.select();
26681                         }
26682                     }
26683                 }
26684                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26685                     this.cleanUpPaste.defer(100, this);
26686                     return;
26687                 }
26688                 
26689                 
26690             };
26691         }else if(Roo.isOpera){
26692             return function(e){
26693                 var k = e.getKey();
26694                 if(k == e.TAB){
26695                     e.stopEvent();
26696                     this.win.focus();
26697                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26698                     this.deferFocus();
26699                 }
26700                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26701                     this.cleanUpPaste.defer(100, this);
26702                     return;
26703                 }
26704                 
26705             };
26706         }else if(Roo.isSafari){
26707             return function(e){
26708                 var k = e.getKey();
26709                 
26710                 if(k == e.TAB){
26711                     e.stopEvent();
26712                     this.execCmd('InsertText','\t');
26713                     this.deferFocus();
26714                     return;
26715                 }
26716                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26717                     this.cleanUpPaste.defer(100, this);
26718                     return;
26719                 }
26720                 
26721              };
26722         }
26723     }(),
26724     
26725     getAllAncestors: function()
26726     {
26727         var p = this.getSelectedNode();
26728         var a = [];
26729         if (!p) {
26730             a.push(p); // push blank onto stack..
26731             p = this.getParentElement();
26732         }
26733         
26734         
26735         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26736             a.push(p);
26737             p = p.parentNode;
26738         }
26739         a.push(this.doc.body);
26740         return a;
26741     },
26742     lastSel : false,
26743     lastSelNode : false,
26744     
26745     
26746     getSelection : function() 
26747     {
26748         this.assignDocWin();
26749         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26750     },
26751     
26752     getSelectedNode: function() 
26753     {
26754         // this may only work on Gecko!!!
26755         
26756         // should we cache this!!!!
26757         
26758         
26759         
26760          
26761         var range = this.createRange(this.getSelection()).cloneRange();
26762         
26763         if (Roo.isIE) {
26764             var parent = range.parentElement();
26765             while (true) {
26766                 var testRange = range.duplicate();
26767                 testRange.moveToElementText(parent);
26768                 if (testRange.inRange(range)) {
26769                     break;
26770                 }
26771                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26772                     break;
26773                 }
26774                 parent = parent.parentElement;
26775             }
26776             return parent;
26777         }
26778         
26779         // is ancestor a text element.
26780         var ac =  range.commonAncestorContainer;
26781         if (ac.nodeType == 3) {
26782             ac = ac.parentNode;
26783         }
26784         
26785         var ar = ac.childNodes;
26786          
26787         var nodes = [];
26788         var other_nodes = [];
26789         var has_other_nodes = false;
26790         for (var i=0;i<ar.length;i++) {
26791             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26792                 continue;
26793             }
26794             // fullly contained node.
26795             
26796             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26797                 nodes.push(ar[i]);
26798                 continue;
26799             }
26800             
26801             // probably selected..
26802             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26803                 other_nodes.push(ar[i]);
26804                 continue;
26805             }
26806             // outer..
26807             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26808                 continue;
26809             }
26810             
26811             
26812             has_other_nodes = true;
26813         }
26814         if (!nodes.length && other_nodes.length) {
26815             nodes= other_nodes;
26816         }
26817         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26818             return false;
26819         }
26820         
26821         return nodes[0];
26822     },
26823     createRange: function(sel)
26824     {
26825         // this has strange effects when using with 
26826         // top toolbar - not sure if it's a great idea.
26827         //this.editor.contentWindow.focus();
26828         if (typeof sel != "undefined") {
26829             try {
26830                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26831             } catch(e) {
26832                 return this.doc.createRange();
26833             }
26834         } else {
26835             return this.doc.createRange();
26836         }
26837     },
26838     getParentElement: function()
26839     {
26840         
26841         this.assignDocWin();
26842         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26843         
26844         var range = this.createRange(sel);
26845          
26846         try {
26847             var p = range.commonAncestorContainer;
26848             while (p.nodeType == 3) { // text node
26849                 p = p.parentNode;
26850             }
26851             return p;
26852         } catch (e) {
26853             return null;
26854         }
26855     
26856     },
26857     /***
26858      *
26859      * Range intersection.. the hard stuff...
26860      *  '-1' = before
26861      *  '0' = hits..
26862      *  '1' = after.
26863      *         [ -- selected range --- ]
26864      *   [fail]                        [fail]
26865      *
26866      *    basically..
26867      *      if end is before start or  hits it. fail.
26868      *      if start is after end or hits it fail.
26869      *
26870      *   if either hits (but other is outside. - then it's not 
26871      *   
26872      *    
26873      **/
26874     
26875     
26876     // @see http://www.thismuchiknow.co.uk/?p=64.
26877     rangeIntersectsNode : function(range, node)
26878     {
26879         var nodeRange = node.ownerDocument.createRange();
26880         try {
26881             nodeRange.selectNode(node);
26882         } catch (e) {
26883             nodeRange.selectNodeContents(node);
26884         }
26885     
26886         var rangeStartRange = range.cloneRange();
26887         rangeStartRange.collapse(true);
26888     
26889         var rangeEndRange = range.cloneRange();
26890         rangeEndRange.collapse(false);
26891     
26892         var nodeStartRange = nodeRange.cloneRange();
26893         nodeStartRange.collapse(true);
26894     
26895         var nodeEndRange = nodeRange.cloneRange();
26896         nodeEndRange.collapse(false);
26897     
26898         return rangeStartRange.compareBoundaryPoints(
26899                  Range.START_TO_START, nodeEndRange) == -1 &&
26900                rangeEndRange.compareBoundaryPoints(
26901                  Range.START_TO_START, nodeStartRange) == 1;
26902         
26903          
26904     },
26905     rangeCompareNode : function(range, node)
26906     {
26907         var nodeRange = node.ownerDocument.createRange();
26908         try {
26909             nodeRange.selectNode(node);
26910         } catch (e) {
26911             nodeRange.selectNodeContents(node);
26912         }
26913         
26914         
26915         range.collapse(true);
26916     
26917         nodeRange.collapse(true);
26918      
26919         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26920         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26921          
26922         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26923         
26924         var nodeIsBefore   =  ss == 1;
26925         var nodeIsAfter    = ee == -1;
26926         
26927         if (nodeIsBefore && nodeIsAfter) {
26928             return 0; // outer
26929         }
26930         if (!nodeIsBefore && nodeIsAfter) {
26931             return 1; //right trailed.
26932         }
26933         
26934         if (nodeIsBefore && !nodeIsAfter) {
26935             return 2;  // left trailed.
26936         }
26937         // fully contined.
26938         return 3;
26939     },
26940
26941     // private? - in a new class?
26942     cleanUpPaste :  function()
26943     {
26944         // cleans up the whole document..
26945         Roo.log('cleanuppaste');
26946         
26947         this.cleanUpChildren(this.doc.body);
26948         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26949         if (clean != this.doc.body.innerHTML) {
26950             this.doc.body.innerHTML = clean;
26951         }
26952         
26953     },
26954     
26955     cleanWordChars : function(input) {// change the chars to hex code
26956         var he = Roo.HtmlEditorCore;
26957         
26958         var output = input;
26959         Roo.each(he.swapCodes, function(sw) { 
26960             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26961             
26962             output = output.replace(swapper, sw[1]);
26963         });
26964         
26965         return output;
26966     },
26967     
26968     
26969     cleanUpChildren : function (n)
26970     {
26971         if (!n.childNodes.length) {
26972             return;
26973         }
26974         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26975            this.cleanUpChild(n.childNodes[i]);
26976         }
26977     },
26978     
26979     
26980         
26981     
26982     cleanUpChild : function (node)
26983     {
26984         var ed = this;
26985         //console.log(node);
26986         if (node.nodeName == "#text") {
26987             // clean up silly Windows -- stuff?
26988             return; 
26989         }
26990         if (node.nodeName == "#comment") {
26991             if (!this.allowComments) {
26992                 node.parentNode.removeChild(node);
26993             }
26994             // clean up silly Windows -- stuff?
26995             return; 
26996         }
26997         var lcname = node.tagName.toLowerCase();
26998         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26999         // whitelist of tags..
27000         
27001         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27002             // remove node.
27003             node.parentNode.removeChild(node);
27004             return;
27005             
27006         }
27007         
27008         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27009         
27010         // spans with no attributes - just remove them..
27011         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27012             remove_keep_children = true;
27013         }
27014         
27015         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27016         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27017         
27018         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27019         //    remove_keep_children = true;
27020         //}
27021         
27022         if (remove_keep_children) {
27023             this.cleanUpChildren(node);
27024             // inserts everything just before this node...
27025             while (node.childNodes.length) {
27026                 var cn = node.childNodes[0];
27027                 node.removeChild(cn);
27028                 node.parentNode.insertBefore(cn, node);
27029             }
27030             node.parentNode.removeChild(node);
27031             return;
27032         }
27033         
27034         if (!node.attributes || !node.attributes.length) {
27035             
27036           
27037             
27038             
27039             this.cleanUpChildren(node);
27040             return;
27041         }
27042         
27043         function cleanAttr(n,v)
27044         {
27045             
27046             if (v.match(/^\./) || v.match(/^\//)) {
27047                 return;
27048             }
27049             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27050                 return;
27051             }
27052             if (v.match(/^#/)) {
27053                 return;
27054             }
27055             if (v.match(/^\{/)) { // allow template editing.
27056                 return;
27057             }
27058 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27059             node.removeAttribute(n);
27060             
27061         }
27062         
27063         var cwhite = this.cwhite;
27064         var cblack = this.cblack;
27065             
27066         function cleanStyle(n,v)
27067         {
27068             if (v.match(/expression/)) { //XSS?? should we even bother..
27069                 node.removeAttribute(n);
27070                 return;
27071             }
27072             
27073             var parts = v.split(/;/);
27074             var clean = [];
27075             
27076             Roo.each(parts, function(p) {
27077                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27078                 if (!p.length) {
27079                     return true;
27080                 }
27081                 var l = p.split(':').shift().replace(/\s+/g,'');
27082                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27083                 
27084                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27085 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27086                     //node.removeAttribute(n);
27087                     return true;
27088                 }
27089                 //Roo.log()
27090                 // only allow 'c whitelisted system attributes'
27091                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27092 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27093                     //node.removeAttribute(n);
27094                     return true;
27095                 }
27096                 
27097                 
27098                  
27099                 
27100                 clean.push(p);
27101                 return true;
27102             });
27103             if (clean.length) { 
27104                 node.setAttribute(n, clean.join(';'));
27105             } else {
27106                 node.removeAttribute(n);
27107             }
27108             
27109         }
27110         
27111         
27112         for (var i = node.attributes.length-1; i > -1 ; i--) {
27113             var a = node.attributes[i];
27114             //console.log(a);
27115             
27116             if (a.name.toLowerCase().substr(0,2)=='on')  {
27117                 node.removeAttribute(a.name);
27118                 continue;
27119             }
27120             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27121                 node.removeAttribute(a.name);
27122                 continue;
27123             }
27124             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27125                 cleanAttr(a.name,a.value); // fixme..
27126                 continue;
27127             }
27128             if (a.name == 'style') {
27129                 cleanStyle(a.name,a.value);
27130                 continue;
27131             }
27132             /// clean up MS crap..
27133             // tecnically this should be a list of valid class'es..
27134             
27135             
27136             if (a.name == 'class') {
27137                 if (a.value.match(/^Mso/)) {
27138                     node.removeAttribute('class');
27139                 }
27140                 
27141                 if (a.value.match(/^body$/)) {
27142                     node.removeAttribute('class');
27143                 }
27144                 continue;
27145             }
27146             
27147             // style cleanup!?
27148             // class cleanup?
27149             
27150         }
27151         
27152         
27153         this.cleanUpChildren(node);
27154         
27155         
27156     },
27157     
27158     /**
27159      * Clean up MS wordisms...
27160      */
27161     cleanWord : function(node)
27162     {
27163         if (!node) {
27164             this.cleanWord(this.doc.body);
27165             return;
27166         }
27167         
27168         if(
27169                 node.nodeName == 'SPAN' &&
27170                 !node.hasAttributes() &&
27171                 node.childNodes.length == 1 &&
27172                 node.firstChild.nodeName == "#text"  
27173         ) {
27174             var textNode = node.firstChild;
27175             node.removeChild(textNode);
27176             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27177                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27178             }
27179             node.parentNode.insertBefore(textNode, node);
27180             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27181                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27182             }
27183             node.parentNode.removeChild(node);
27184         }
27185         
27186         if (node.nodeName == "#text") {
27187             // clean up silly Windows -- stuff?
27188             return; 
27189         }
27190         if (node.nodeName == "#comment") {
27191             node.parentNode.removeChild(node);
27192             // clean up silly Windows -- stuff?
27193             return; 
27194         }
27195         
27196         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27197             node.parentNode.removeChild(node);
27198             return;
27199         }
27200         //Roo.log(node.tagName);
27201         // remove - but keep children..
27202         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27203             //Roo.log('-- removed');
27204             while (node.childNodes.length) {
27205                 var cn = node.childNodes[0];
27206                 node.removeChild(cn);
27207                 node.parentNode.insertBefore(cn, node);
27208                 // move node to parent - and clean it..
27209                 this.cleanWord(cn);
27210             }
27211             node.parentNode.removeChild(node);
27212             /// no need to iterate chidlren = it's got none..
27213             //this.iterateChildren(node, this.cleanWord);
27214             return;
27215         }
27216         // clean styles
27217         if (node.className.length) {
27218             
27219             var cn = node.className.split(/\W+/);
27220             var cna = [];
27221             Roo.each(cn, function(cls) {
27222                 if (cls.match(/Mso[a-zA-Z]+/)) {
27223                     return;
27224                 }
27225                 cna.push(cls);
27226             });
27227             node.className = cna.length ? cna.join(' ') : '';
27228             if (!cna.length) {
27229                 node.removeAttribute("class");
27230             }
27231         }
27232         
27233         if (node.hasAttribute("lang")) {
27234             node.removeAttribute("lang");
27235         }
27236         
27237         if (node.hasAttribute("style")) {
27238             
27239             var styles = node.getAttribute("style").split(";");
27240             var nstyle = [];
27241             Roo.each(styles, function(s) {
27242                 if (!s.match(/:/)) {
27243                     return;
27244                 }
27245                 var kv = s.split(":");
27246                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27247                     return;
27248                 }
27249                 // what ever is left... we allow.
27250                 nstyle.push(s);
27251             });
27252             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27253             if (!nstyle.length) {
27254                 node.removeAttribute('style');
27255             }
27256         }
27257         this.iterateChildren(node, this.cleanWord);
27258         
27259         
27260         
27261     },
27262     /**
27263      * iterateChildren of a Node, calling fn each time, using this as the scole..
27264      * @param {DomNode} node node to iterate children of.
27265      * @param {Function} fn method of this class to call on each item.
27266      */
27267     iterateChildren : function(node, fn)
27268     {
27269         if (!node.childNodes.length) {
27270                 return;
27271         }
27272         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27273            fn.call(this, node.childNodes[i])
27274         }
27275     },
27276     
27277     
27278     /**
27279      * cleanTableWidths.
27280      *
27281      * Quite often pasting from word etc.. results in tables with column and widths.
27282      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27283      *
27284      */
27285     cleanTableWidths : function(node)
27286     {
27287          
27288          
27289         if (!node) {
27290             this.cleanTableWidths(this.doc.body);
27291             return;
27292         }
27293         
27294         // ignore list...
27295         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27296             return; 
27297         }
27298         Roo.log(node.tagName);
27299         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27300             this.iterateChildren(node, this.cleanTableWidths);
27301             return;
27302         }
27303         if (node.hasAttribute('width')) {
27304             node.removeAttribute('width');
27305         }
27306         
27307          
27308         if (node.hasAttribute("style")) {
27309             // pretty basic...
27310             
27311             var styles = node.getAttribute("style").split(";");
27312             var nstyle = [];
27313             Roo.each(styles, function(s) {
27314                 if (!s.match(/:/)) {
27315                     return;
27316                 }
27317                 var kv = s.split(":");
27318                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27319                     return;
27320                 }
27321                 // what ever is left... we allow.
27322                 nstyle.push(s);
27323             });
27324             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27325             if (!nstyle.length) {
27326                 node.removeAttribute('style');
27327             }
27328         }
27329         
27330         this.iterateChildren(node, this.cleanTableWidths);
27331         
27332         
27333     },
27334     
27335     
27336     
27337     
27338     domToHTML : function(currentElement, depth, nopadtext) {
27339         
27340         depth = depth || 0;
27341         nopadtext = nopadtext || false;
27342     
27343         if (!currentElement) {
27344             return this.domToHTML(this.doc.body);
27345         }
27346         
27347         //Roo.log(currentElement);
27348         var j;
27349         var allText = false;
27350         var nodeName = currentElement.nodeName;
27351         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27352         
27353         if  (nodeName == '#text') {
27354             
27355             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27356         }
27357         
27358         
27359         var ret = '';
27360         if (nodeName != 'BODY') {
27361              
27362             var i = 0;
27363             // Prints the node tagName, such as <A>, <IMG>, etc
27364             if (tagName) {
27365                 var attr = [];
27366                 for(i = 0; i < currentElement.attributes.length;i++) {
27367                     // quoting?
27368                     var aname = currentElement.attributes.item(i).name;
27369                     if (!currentElement.attributes.item(i).value.length) {
27370                         continue;
27371                     }
27372                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27373                 }
27374                 
27375                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27376             } 
27377             else {
27378                 
27379                 // eack
27380             }
27381         } else {
27382             tagName = false;
27383         }
27384         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27385             return ret;
27386         }
27387         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27388             nopadtext = true;
27389         }
27390         
27391         
27392         // Traverse the tree
27393         i = 0;
27394         var currentElementChild = currentElement.childNodes.item(i);
27395         var allText = true;
27396         var innerHTML  = '';
27397         lastnode = '';
27398         while (currentElementChild) {
27399             // Formatting code (indent the tree so it looks nice on the screen)
27400             var nopad = nopadtext;
27401             if (lastnode == 'SPAN') {
27402                 nopad  = true;
27403             }
27404             // text
27405             if  (currentElementChild.nodeName == '#text') {
27406                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27407                 toadd = nopadtext ? toadd : toadd.trim();
27408                 if (!nopad && toadd.length > 80) {
27409                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27410                 }
27411                 innerHTML  += toadd;
27412                 
27413                 i++;
27414                 currentElementChild = currentElement.childNodes.item(i);
27415                 lastNode = '';
27416                 continue;
27417             }
27418             allText = false;
27419             
27420             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27421                 
27422             // Recursively traverse the tree structure of the child node
27423             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27424             lastnode = currentElementChild.nodeName;
27425             i++;
27426             currentElementChild=currentElement.childNodes.item(i);
27427         }
27428         
27429         ret += innerHTML;
27430         
27431         if (!allText) {
27432                 // The remaining code is mostly for formatting the tree
27433             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27434         }
27435         
27436         
27437         if (tagName) {
27438             ret+= "</"+tagName+">";
27439         }
27440         return ret;
27441         
27442     },
27443         
27444     applyBlacklists : function()
27445     {
27446         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27447         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27448         
27449         this.white = [];
27450         this.black = [];
27451         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27452             if (b.indexOf(tag) > -1) {
27453                 return;
27454             }
27455             this.white.push(tag);
27456             
27457         }, this);
27458         
27459         Roo.each(w, function(tag) {
27460             if (b.indexOf(tag) > -1) {
27461                 return;
27462             }
27463             if (this.white.indexOf(tag) > -1) {
27464                 return;
27465             }
27466             this.white.push(tag);
27467             
27468         }, this);
27469         
27470         
27471         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27472             if (w.indexOf(tag) > -1) {
27473                 return;
27474             }
27475             this.black.push(tag);
27476             
27477         }, this);
27478         
27479         Roo.each(b, function(tag) {
27480             if (w.indexOf(tag) > -1) {
27481                 return;
27482             }
27483             if (this.black.indexOf(tag) > -1) {
27484                 return;
27485             }
27486             this.black.push(tag);
27487             
27488         }, this);
27489         
27490         
27491         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27492         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27493         
27494         this.cwhite = [];
27495         this.cblack = [];
27496         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27497             if (b.indexOf(tag) > -1) {
27498                 return;
27499             }
27500             this.cwhite.push(tag);
27501             
27502         }, this);
27503         
27504         Roo.each(w, function(tag) {
27505             if (b.indexOf(tag) > -1) {
27506                 return;
27507             }
27508             if (this.cwhite.indexOf(tag) > -1) {
27509                 return;
27510             }
27511             this.cwhite.push(tag);
27512             
27513         }, this);
27514         
27515         
27516         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27517             if (w.indexOf(tag) > -1) {
27518                 return;
27519             }
27520             this.cblack.push(tag);
27521             
27522         }, this);
27523         
27524         Roo.each(b, function(tag) {
27525             if (w.indexOf(tag) > -1) {
27526                 return;
27527             }
27528             if (this.cblack.indexOf(tag) > -1) {
27529                 return;
27530             }
27531             this.cblack.push(tag);
27532             
27533         }, this);
27534     },
27535     
27536     setStylesheets : function(stylesheets)
27537     {
27538         if(typeof(stylesheets) == 'string'){
27539             Roo.get(this.iframe.contentDocument.head).createChild({
27540                 tag : 'link',
27541                 rel : 'stylesheet',
27542                 type : 'text/css',
27543                 href : stylesheets
27544             });
27545             
27546             return;
27547         }
27548         var _this = this;
27549      
27550         Roo.each(stylesheets, function(s) {
27551             if(!s.length){
27552                 return;
27553             }
27554             
27555             Roo.get(_this.iframe.contentDocument.head).createChild({
27556                 tag : 'link',
27557                 rel : 'stylesheet',
27558                 type : 'text/css',
27559                 href : s
27560             });
27561         });
27562
27563         
27564     },
27565     
27566     removeStylesheets : function()
27567     {
27568         var _this = this;
27569         
27570         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27571             s.remove();
27572         });
27573     },
27574     
27575     setStyle : function(style)
27576     {
27577         Roo.get(this.iframe.contentDocument.head).createChild({
27578             tag : 'style',
27579             type : 'text/css',
27580             html : style
27581         });
27582
27583         return;
27584     }
27585     
27586     // hide stuff that is not compatible
27587     /**
27588      * @event blur
27589      * @hide
27590      */
27591     /**
27592      * @event change
27593      * @hide
27594      */
27595     /**
27596      * @event focus
27597      * @hide
27598      */
27599     /**
27600      * @event specialkey
27601      * @hide
27602      */
27603     /**
27604      * @cfg {String} fieldClass @hide
27605      */
27606     /**
27607      * @cfg {String} focusClass @hide
27608      */
27609     /**
27610      * @cfg {String} autoCreate @hide
27611      */
27612     /**
27613      * @cfg {String} inputType @hide
27614      */
27615     /**
27616      * @cfg {String} invalidClass @hide
27617      */
27618     /**
27619      * @cfg {String} invalidText @hide
27620      */
27621     /**
27622      * @cfg {String} msgFx @hide
27623      */
27624     /**
27625      * @cfg {String} validateOnBlur @hide
27626      */
27627 });
27628
27629 Roo.HtmlEditorCore.white = [
27630         'area', 'br', 'img', 'input', 'hr', 'wbr',
27631         
27632        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27633        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27634        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27635        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27636        'table',   'ul',         'xmp', 
27637        
27638        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27639       'thead',   'tr', 
27640      
27641       'dir', 'menu', 'ol', 'ul', 'dl',
27642        
27643       'embed',  'object'
27644 ];
27645
27646
27647 Roo.HtmlEditorCore.black = [
27648     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27649         'applet', // 
27650         'base',   'basefont', 'bgsound', 'blink',  'body', 
27651         'frame',  'frameset', 'head',    'html',   'ilayer', 
27652         'iframe', 'layer',  'link',     'meta',    'object',   
27653         'script', 'style' ,'title',  'xml' // clean later..
27654 ];
27655 Roo.HtmlEditorCore.clean = [
27656     'script', 'style', 'title', 'xml'
27657 ];
27658 Roo.HtmlEditorCore.remove = [
27659     'font'
27660 ];
27661 // attributes..
27662
27663 Roo.HtmlEditorCore.ablack = [
27664     'on'
27665 ];
27666     
27667 Roo.HtmlEditorCore.aclean = [ 
27668     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27669 ];
27670
27671 // protocols..
27672 Roo.HtmlEditorCore.pwhite= [
27673         'http',  'https',  'mailto'
27674 ];
27675
27676 // white listed style attributes.
27677 Roo.HtmlEditorCore.cwhite= [
27678       //  'text-align', /// default is to allow most things..
27679       
27680          
27681 //        'font-size'//??
27682 ];
27683
27684 // black listed style attributes.
27685 Roo.HtmlEditorCore.cblack= [
27686       //  'font-size' -- this can be set by the project 
27687 ];
27688
27689
27690 Roo.HtmlEditorCore.swapCodes   =[ 
27691     [    8211, "&#8211;" ], 
27692     [    8212, "&#8212;" ], 
27693     [    8216,  "'" ],  
27694     [    8217, "'" ],  
27695     [    8220, '"' ],  
27696     [    8221, '"' ],  
27697     [    8226, "*" ],  
27698     [    8230, "..." ]
27699 ]; 
27700
27701     /*
27702  * - LGPL
27703  *
27704  * HtmlEditor
27705  * 
27706  */
27707
27708 /**
27709  * @class Roo.bootstrap.form.HtmlEditor
27710  * @extends Roo.bootstrap.form.TextArea
27711  * Bootstrap HtmlEditor class
27712
27713  * @constructor
27714  * Create a new HtmlEditor
27715  * @param {Object} config The config object
27716  */
27717
27718 Roo.bootstrap.form.HtmlEditor = function(config){
27719     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27720     if (!this.toolbars) {
27721         this.toolbars = [];
27722     }
27723     
27724     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27725     this.addEvents({
27726             /**
27727              * @event initialize
27728              * Fires when the editor is fully initialized (including the iframe)
27729              * @param {HtmlEditor} this
27730              */
27731             initialize: true,
27732             /**
27733              * @event activate
27734              * Fires when the editor is first receives the focus. Any insertion must wait
27735              * until after this event.
27736              * @param {HtmlEditor} this
27737              */
27738             activate: true,
27739              /**
27740              * @event beforesync
27741              * Fires before the textarea is updated with content from the editor iframe. Return false
27742              * to cancel the sync.
27743              * @param {HtmlEditor} this
27744              * @param {String} html
27745              */
27746             beforesync: true,
27747              /**
27748              * @event beforepush
27749              * Fires before the iframe editor is updated with content from the textarea. Return false
27750              * to cancel the push.
27751              * @param {HtmlEditor} this
27752              * @param {String} html
27753              */
27754             beforepush: true,
27755              /**
27756              * @event sync
27757              * Fires when the textarea is updated with content from the editor iframe.
27758              * @param {HtmlEditor} this
27759              * @param {String} html
27760              */
27761             sync: true,
27762              /**
27763              * @event push
27764              * Fires when the iframe editor is updated with content from the textarea.
27765              * @param {HtmlEditor} this
27766              * @param {String} html
27767              */
27768             push: true,
27769              /**
27770              * @event editmodechange
27771              * Fires when the editor switches edit modes
27772              * @param {HtmlEditor} this
27773              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27774              */
27775             editmodechange: true,
27776             /**
27777              * @event editorevent
27778              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27779              * @param {HtmlEditor} this
27780              */
27781             editorevent: true,
27782             /**
27783              * @event firstfocus
27784              * Fires when on first focus - needed by toolbars..
27785              * @param {HtmlEditor} this
27786              */
27787             firstfocus: true,
27788             /**
27789              * @event autosave
27790              * Auto save the htmlEditor value as a file into Events
27791              * @param {HtmlEditor} this
27792              */
27793             autosave: true,
27794             /**
27795              * @event savedpreview
27796              * preview the saved version of htmlEditor
27797              * @param {HtmlEditor} this
27798              */
27799             savedpreview: true
27800         });
27801 };
27802
27803
27804 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27805     
27806     
27807       /**
27808      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27809      */
27810     toolbars : false,
27811     
27812      /**
27813     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27814     */
27815     btns : [],
27816    
27817      /**
27818      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27819      *                        Roo.resizable.
27820      */
27821     resizable : false,
27822      /**
27823      * @cfg {Number} height (in pixels)
27824      */   
27825     height: 300,
27826    /**
27827      * @cfg {Number} width (in pixels)
27828      */   
27829     width: false,
27830     
27831     /**
27832      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27833      * 
27834      */
27835     stylesheets: false,
27836     
27837     // id of frame..
27838     frameId: false,
27839     
27840     // private properties
27841     validationEvent : false,
27842     deferHeight: true,
27843     initialized : false,
27844     activated : false,
27845     
27846     onFocus : Roo.emptyFn,
27847     iframePad:3,
27848     hideMode:'offsets',
27849     
27850     tbContainer : false,
27851     
27852     bodyCls : '',
27853     
27854     toolbarContainer :function() {
27855         return this.wrap.select('.x-html-editor-tb',true).first();
27856     },
27857
27858     /**
27859      * Protected method that will not generally be called directly. It
27860      * is called when the editor creates its toolbar. Override this method if you need to
27861      * add custom toolbar buttons.
27862      * @param {HtmlEditor} editor
27863      */
27864     createToolbar : function(){
27865         Roo.log('renewing');
27866         Roo.log("create toolbars");
27867         
27868         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27869         this.toolbars[0].render(this.toolbarContainer());
27870         
27871         return;
27872         
27873 //        if (!editor.toolbars || !editor.toolbars.length) {
27874 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27875 //        }
27876 //        
27877 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27878 //            editor.toolbars[i] = Roo.factory(
27879 //                    typeof(editor.toolbars[i]) == 'string' ?
27880 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27881 //                Roo.bootstrap.form.HtmlEditor);
27882 //            editor.toolbars[i].init(editor);
27883 //        }
27884     },
27885
27886      
27887     // private
27888     onRender : function(ct, position)
27889     {
27890        // Roo.log("Call onRender: " + this.xtype);
27891         var _t = this;
27892         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27893       
27894         this.wrap = this.inputEl().wrap({
27895             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27896         });
27897         
27898         this.editorcore.onRender(ct, position);
27899          
27900         if (this.resizable) {
27901             this.resizeEl = new Roo.Resizable(this.wrap, {
27902                 pinned : true,
27903                 wrap: true,
27904                 dynamic : true,
27905                 minHeight : this.height,
27906                 height: this.height,
27907                 handles : this.resizable,
27908                 width: this.width,
27909                 listeners : {
27910                     resize : function(r, w, h) {
27911                         _t.onResize(w,h); // -something
27912                     }
27913                 }
27914             });
27915             
27916         }
27917         this.createToolbar(this);
27918        
27919         
27920         if(!this.width && this.resizable){
27921             this.setSize(this.wrap.getSize());
27922         }
27923         if (this.resizeEl) {
27924             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27925             // should trigger onReize..
27926         }
27927         
27928     },
27929
27930     // private
27931     onResize : function(w, h)
27932     {
27933         Roo.log('resize: ' +w + ',' + h );
27934         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27935         var ew = false;
27936         var eh = false;
27937         
27938         if(this.inputEl() ){
27939             if(typeof w == 'number'){
27940                 var aw = w - this.wrap.getFrameWidth('lr');
27941                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27942                 ew = aw;
27943             }
27944             if(typeof h == 'number'){
27945                  var tbh = -11;  // fixme it needs to tool bar size!
27946                 for (var i =0; i < this.toolbars.length;i++) {
27947                     // fixme - ask toolbars for heights?
27948                     tbh += this.toolbars[i].el.getHeight();
27949                     //if (this.toolbars[i].footer) {
27950                     //    tbh += this.toolbars[i].footer.el.getHeight();
27951                     //}
27952                 }
27953               
27954                 
27955                 
27956                 
27957                 
27958                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27959                 ah -= 5; // knock a few pixes off for look..
27960                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27961                 var eh = ah;
27962             }
27963         }
27964         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27965         this.editorcore.onResize(ew,eh);
27966         
27967     },
27968
27969     /**
27970      * Toggles the editor between standard and source edit mode.
27971      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27972      */
27973     toggleSourceEdit : function(sourceEditMode)
27974     {
27975         this.editorcore.toggleSourceEdit(sourceEditMode);
27976         
27977         if(this.editorcore.sourceEditMode){
27978             Roo.log('editor - showing textarea');
27979             
27980 //            Roo.log('in');
27981 //            Roo.log(this.syncValue());
27982             this.syncValue();
27983             this.inputEl().removeClass(['hide', 'x-hidden']);
27984             this.inputEl().dom.removeAttribute('tabIndex');
27985             this.inputEl().focus();
27986         }else{
27987             Roo.log('editor - hiding textarea');
27988 //            Roo.log('out')
27989 //            Roo.log(this.pushValue()); 
27990             this.pushValue();
27991             
27992             this.inputEl().addClass(['hide', 'x-hidden']);
27993             this.inputEl().dom.setAttribute('tabIndex', -1);
27994             //this.deferFocus();
27995         }
27996          
27997         if(this.resizable){
27998             this.setSize(this.wrap.getSize());
27999         }
28000         
28001         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28002     },
28003  
28004     // private (for BoxComponent)
28005     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28006
28007     // private (for BoxComponent)
28008     getResizeEl : function(){
28009         return this.wrap;
28010     },
28011
28012     // private (for BoxComponent)
28013     getPositionEl : function(){
28014         return this.wrap;
28015     },
28016
28017     // private
28018     initEvents : function(){
28019         this.originalValue = this.getValue();
28020     },
28021
28022 //    /**
28023 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28024 //     * @method
28025 //     */
28026 //    markInvalid : Roo.emptyFn,
28027 //    /**
28028 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28029 //     * @method
28030 //     */
28031 //    clearInvalid : Roo.emptyFn,
28032
28033     setValue : function(v){
28034         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28035         this.editorcore.pushValue();
28036     },
28037
28038      
28039     // private
28040     deferFocus : function(){
28041         this.focus.defer(10, this);
28042     },
28043
28044     // doc'ed in Field
28045     focus : function(){
28046         this.editorcore.focus();
28047         
28048     },
28049       
28050
28051     // private
28052     onDestroy : function(){
28053         
28054         
28055         
28056         if(this.rendered){
28057             
28058             for (var i =0; i < this.toolbars.length;i++) {
28059                 // fixme - ask toolbars for heights?
28060                 this.toolbars[i].onDestroy();
28061             }
28062             
28063             this.wrap.dom.innerHTML = '';
28064             this.wrap.remove();
28065         }
28066     },
28067
28068     // private
28069     onFirstFocus : function(){
28070         //Roo.log("onFirstFocus");
28071         this.editorcore.onFirstFocus();
28072          for (var i =0; i < this.toolbars.length;i++) {
28073             this.toolbars[i].onFirstFocus();
28074         }
28075         
28076     },
28077     
28078     // private
28079     syncValue : function()
28080     {   
28081         this.editorcore.syncValue();
28082     },
28083     
28084     pushValue : function()
28085     {   
28086         this.editorcore.pushValue();
28087     }
28088      
28089     
28090     // hide stuff that is not compatible
28091     /**
28092      * @event blur
28093      * @hide
28094      */
28095     /**
28096      * @event change
28097      * @hide
28098      */
28099     /**
28100      * @event focus
28101      * @hide
28102      */
28103     /**
28104      * @event specialkey
28105      * @hide
28106      */
28107     /**
28108      * @cfg {String} fieldClass @hide
28109      */
28110     /**
28111      * @cfg {String} focusClass @hide
28112      */
28113     /**
28114      * @cfg {String} autoCreate @hide
28115      */
28116     /**
28117      * @cfg {String} inputType @hide
28118      */
28119      
28120     /**
28121      * @cfg {String} invalidText @hide
28122      */
28123     /**
28124      * @cfg {String} msgFx @hide
28125      */
28126     /**
28127      * @cfg {String} validateOnBlur @hide
28128      */
28129 });
28130  
28131     
28132    
28133    
28134    
28135       
28136 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28137 /**
28138  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28139  * @parent Roo.bootstrap.form.HtmlEditor
28140  * @extends Roo.bootstrap.nav.Simplebar
28141  * Basic Toolbar
28142  * 
28143  * @example
28144  * Usage:
28145  *
28146  new Roo.bootstrap.form.HtmlEditor({
28147     ....
28148     toolbars : [
28149         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28150             disable : { fonts: 1 , format: 1, ..., ... , ...],
28151             btns : [ .... ]
28152         })
28153     }
28154      
28155  * 
28156  * @cfg {Object} disable List of elements to disable..
28157  * @cfg {Array} btns List of additional buttons.
28158  * 
28159  * 
28160  * NEEDS Extra CSS? 
28161  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28162  */
28163  
28164 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28165 {
28166     
28167     Roo.apply(this, config);
28168     
28169     // default disabled, based on 'good practice'..
28170     this.disable = this.disable || {};
28171     Roo.applyIf(this.disable, {
28172         fontSize : true,
28173         colors : true,
28174         specialElements : true
28175     });
28176     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28177     
28178     this.editor = config.editor;
28179     this.editorcore = config.editor.editorcore;
28180     
28181     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28182     
28183     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28184     // dont call parent... till later.
28185 }
28186 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28187      
28188     bar : true,
28189     
28190     editor : false,
28191     editorcore : false,
28192     
28193     
28194     formats : [
28195         "p" ,  
28196         "h1","h2","h3","h4","h5","h6", 
28197         "pre", "code", 
28198         "abbr", "acronym", "address", "cite", "samp", "var",
28199         'div','span'
28200     ],
28201     
28202     onRender : function(ct, position)
28203     {
28204        // Roo.log("Call onRender: " + this.xtype);
28205         
28206        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28207        Roo.log(this.el);
28208        this.el.dom.style.marginBottom = '0';
28209        var _this = this;
28210        var editorcore = this.editorcore;
28211        var editor= this.editor;
28212        
28213        var children = [];
28214        var btn = function(id,cmd , toggle, handler, html){
28215        
28216             var  event = toggle ? 'toggle' : 'click';
28217        
28218             var a = {
28219                 size : 'sm',
28220                 xtype: 'Button',
28221                 xns: Roo.bootstrap,
28222                 //glyphicon : id,
28223                 fa: id,
28224                 cmd : id || cmd,
28225                 enableToggle:toggle !== false,
28226                 html : html || '',
28227                 pressed : toggle ? false : null,
28228                 listeners : {}
28229             };
28230             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28231                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28232             };
28233             children.push(a);
28234             return a;
28235        }
28236        
28237     //    var cb_box = function...
28238         
28239         var style = {
28240                 xtype: 'Button',
28241                 size : 'sm',
28242                 xns: Roo.bootstrap,
28243                 fa : 'font',
28244                 //html : 'submit'
28245                 menu : {
28246                     xtype: 'Menu',
28247                     xns: Roo.bootstrap,
28248                     items:  []
28249                 }
28250         };
28251         Roo.each(this.formats, function(f) {
28252             style.menu.items.push({
28253                 xtype :'MenuItem',
28254                 xns: Roo.bootstrap,
28255                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28256                 tagname : f,
28257                 listeners : {
28258                     click : function()
28259                     {
28260                         editorcore.insertTag(this.tagname);
28261                         editor.focus();
28262                     }
28263                 }
28264                 
28265             });
28266         });
28267         children.push(style);   
28268         
28269         btn('bold',false,true);
28270         btn('italic',false,true);
28271         btn('align-left', 'justifyleft',true);
28272         btn('align-center', 'justifycenter',true);
28273         btn('align-right' , 'justifyright',true);
28274         btn('link', false, false, function(btn) {
28275             //Roo.log("create link?");
28276             var url = prompt(this.createLinkText, this.defaultLinkValue);
28277             if(url && url != 'http:/'+'/'){
28278                 this.editorcore.relayCmd('createlink', url);
28279             }
28280         }),
28281         btn('list','insertunorderedlist',true);
28282         btn('pencil', false,true, function(btn){
28283                 Roo.log(this);
28284                 this.toggleSourceEdit(btn.pressed);
28285         });
28286         
28287         if (this.editor.btns.length > 0) {
28288             for (var i = 0; i<this.editor.btns.length; i++) {
28289                 children.push(this.editor.btns[i]);
28290             }
28291         }
28292         
28293         /*
28294         var cog = {
28295                 xtype: 'Button',
28296                 size : 'sm',
28297                 xns: Roo.bootstrap,
28298                 glyphicon : 'cog',
28299                 //html : 'submit'
28300                 menu : {
28301                     xtype: 'Menu',
28302                     xns: Roo.bootstrap,
28303                     items:  []
28304                 }
28305         };
28306         
28307         cog.menu.items.push({
28308             xtype :'MenuItem',
28309             xns: Roo.bootstrap,
28310             html : Clean styles,
28311             tagname : f,
28312             listeners : {
28313                 click : function()
28314                 {
28315                     editorcore.insertTag(this.tagname);
28316                     editor.focus();
28317                 }
28318             }
28319             
28320         });
28321        */
28322         
28323          
28324        this.xtype = 'NavSimplebar';
28325         
28326         for(var i=0;i< children.length;i++) {
28327             
28328             this.buttons.add(this.addxtypeChild(children[i]));
28329             
28330         }
28331         
28332         editor.on('editorevent', this.updateToolbar, this);
28333     },
28334     onBtnClick : function(id)
28335     {
28336        this.editorcore.relayCmd(id);
28337        this.editorcore.focus();
28338     },
28339     
28340     /**
28341      * Protected method that will not generally be called directly. It triggers
28342      * a toolbar update by reading the markup state of the current selection in the editor.
28343      */
28344     updateToolbar: function(){
28345
28346         if(!this.editorcore.activated){
28347             this.editor.onFirstFocus(); // is this neeed?
28348             return;
28349         }
28350
28351         var btns = this.buttons; 
28352         var doc = this.editorcore.doc;
28353         btns.get('bold').setActive(doc.queryCommandState('bold'));
28354         btns.get('italic').setActive(doc.queryCommandState('italic'));
28355         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28356         
28357         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28358         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28359         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28360         
28361         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28362         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28363          /*
28364         
28365         var ans = this.editorcore.getAllAncestors();
28366         if (this.formatCombo) {
28367             
28368             
28369             var store = this.formatCombo.store;
28370             this.formatCombo.setValue("");
28371             for (var i =0; i < ans.length;i++) {
28372                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28373                     // select it..
28374                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28375                     break;
28376                 }
28377             }
28378         }
28379         
28380         
28381         
28382         // hides menus... - so this cant be on a menu...
28383         Roo.bootstrap.MenuMgr.hideAll();
28384         */
28385         Roo.bootstrap.menu.Manager.hideAll();
28386         //this.editorsyncValue();
28387     },
28388     onFirstFocus: function() {
28389         this.buttons.each(function(item){
28390            item.enable();
28391         });
28392     },
28393     toggleSourceEdit : function(sourceEditMode){
28394         
28395           
28396         if(sourceEditMode){
28397             Roo.log("disabling buttons");
28398            this.buttons.each( function(item){
28399                 if(item.cmd != 'pencil'){
28400                     item.disable();
28401                 }
28402             });
28403           
28404         }else{
28405             Roo.log("enabling buttons");
28406             if(this.editorcore.initialized){
28407                 this.buttons.each( function(item){
28408                     item.enable();
28409                 });
28410             }
28411             
28412         }
28413         Roo.log("calling toggole on editor");
28414         // tell the editor that it's been pressed..
28415         this.editor.toggleSourceEdit(sourceEditMode);
28416        
28417     }
28418 });
28419
28420
28421
28422
28423  
28424 /*
28425  * - LGPL
28426  */
28427
28428 /**
28429  * @class Roo.bootstrap.form.Markdown
28430  * @extends Roo.bootstrap.form.TextArea
28431  * Bootstrap Showdown editable area
28432  * @cfg {string} content
28433  * 
28434  * @constructor
28435  * Create a new Showdown
28436  */
28437
28438 Roo.bootstrap.form.Markdown = function(config){
28439     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28440    
28441 };
28442
28443 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28444     
28445     editing :false,
28446     
28447     initEvents : function()
28448     {
28449         
28450         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28451         this.markdownEl = this.el.createChild({
28452             cls : 'roo-markdown-area'
28453         });
28454         this.inputEl().addClass('d-none');
28455         if (this.getValue() == '') {
28456             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28457             
28458         } else {
28459             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28460         }
28461         this.markdownEl.on('click', this.toggleTextEdit, this);
28462         this.on('blur', this.toggleTextEdit, this);
28463         this.on('specialkey', this.resizeTextArea, this);
28464     },
28465     
28466     toggleTextEdit : function()
28467     {
28468         var sh = this.markdownEl.getHeight();
28469         this.inputEl().addClass('d-none');
28470         this.markdownEl.addClass('d-none');
28471         if (!this.editing) {
28472             // show editor?
28473             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28474             this.inputEl().removeClass('d-none');
28475             this.inputEl().focus();
28476             this.editing = true;
28477             return;
28478         }
28479         // show showdown...
28480         this.updateMarkdown();
28481         this.markdownEl.removeClass('d-none');
28482         this.editing = false;
28483         return;
28484     },
28485     updateMarkdown : function()
28486     {
28487         if (this.getValue() == '') {
28488             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28489             return;
28490         }
28491  
28492         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28493     },
28494     
28495     resizeTextArea: function () {
28496         
28497         var sh = 100;
28498         Roo.log([sh, this.getValue().split("\n").length * 30]);
28499         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28500     },
28501     setValue : function(val)
28502     {
28503         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28504         if (!this.editing) {
28505             this.updateMarkdown();
28506         }
28507         
28508     },
28509     focus : function()
28510     {
28511         if (!this.editing) {
28512             this.toggleTextEdit();
28513         }
28514         
28515     }
28516
28517
28518 });/*
28519  * Based on:
28520  * Ext JS Library 1.1.1
28521  * Copyright(c) 2006-2007, Ext JS, LLC.
28522  *
28523  * Originally Released Under LGPL - original licence link has changed is not relivant.
28524  *
28525  * Fork - LGPL
28526  * <script type="text/javascript">
28527  */
28528  
28529 /**
28530  * @class Roo.bootstrap.PagingToolbar
28531  * @extends Roo.bootstrap.nav.Simplebar
28532  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28533  * @constructor
28534  * Create a new PagingToolbar
28535  * @param {Object} config The config object
28536  * @param {Roo.data.Store} store
28537  */
28538 Roo.bootstrap.PagingToolbar = function(config)
28539 {
28540     // old args format still supported... - xtype is prefered..
28541         // created from xtype...
28542     
28543     this.ds = config.dataSource;
28544     
28545     if (config.store && !this.ds) {
28546         this.store= Roo.factory(config.store, Roo.data);
28547         this.ds = this.store;
28548         this.ds.xmodule = this.xmodule || false;
28549     }
28550     
28551     this.toolbarItems = [];
28552     if (config.items) {
28553         this.toolbarItems = config.items;
28554     }
28555     
28556     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28557     
28558     this.cursor = 0;
28559     
28560     if (this.ds) { 
28561         this.bind(this.ds);
28562     }
28563     
28564     if (Roo.bootstrap.version == 4) {
28565         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28566     } else {
28567         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28568     }
28569     
28570 };
28571
28572 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28573     /**
28574      * @cfg {Roo.bootstrap.Button} buttons[]
28575      * Buttons for the toolbar
28576      */
28577      /**
28578      * @cfg {Roo.data.Store} store
28579      * The underlying data store providing the paged data
28580      */
28581     /**
28582      * @cfg {String/HTMLElement/Element} container
28583      * container The id or element that will contain the toolbar
28584      */
28585     /**
28586      * @cfg {Boolean} displayInfo
28587      * True to display the displayMsg (defaults to false)
28588      */
28589     /**
28590      * @cfg {Number} pageSize
28591      * The number of records to display per page (defaults to 20)
28592      */
28593     pageSize: 20,
28594     /**
28595      * @cfg {String} displayMsg
28596      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28597      */
28598     displayMsg : 'Displaying {0} - {1} of {2}',
28599     /**
28600      * @cfg {String} emptyMsg
28601      * The message to display when no records are found (defaults to "No data to display")
28602      */
28603     emptyMsg : 'No data to display',
28604     /**
28605      * Customizable piece of the default paging text (defaults to "Page")
28606      * @type String
28607      */
28608     beforePageText : "Page",
28609     /**
28610      * Customizable piece of the default paging text (defaults to "of %0")
28611      * @type String
28612      */
28613     afterPageText : "of {0}",
28614     /**
28615      * Customizable piece of the default paging text (defaults to "First Page")
28616      * @type String
28617      */
28618     firstText : "First Page",
28619     /**
28620      * Customizable piece of the default paging text (defaults to "Previous Page")
28621      * @type String
28622      */
28623     prevText : "Previous Page",
28624     /**
28625      * Customizable piece of the default paging text (defaults to "Next Page")
28626      * @type String
28627      */
28628     nextText : "Next Page",
28629     /**
28630      * Customizable piece of the default paging text (defaults to "Last Page")
28631      * @type String
28632      */
28633     lastText : "Last Page",
28634     /**
28635      * Customizable piece of the default paging text (defaults to "Refresh")
28636      * @type String
28637      */
28638     refreshText : "Refresh",
28639
28640     buttons : false,
28641     // private
28642     onRender : function(ct, position) 
28643     {
28644         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28645         this.navgroup.parentId = this.id;
28646         this.navgroup.onRender(this.el, null);
28647         // add the buttons to the navgroup
28648         
28649         if(this.displayInfo){
28650             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28651             this.displayEl = this.el.select('.x-paging-info', true).first();
28652 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28653 //            this.displayEl = navel.el.select('span',true).first();
28654         }
28655         
28656         var _this = this;
28657         
28658         if(this.buttons){
28659             Roo.each(_this.buttons, function(e){ // this might need to use render????
28660                Roo.factory(e).render(_this.el);
28661             });
28662         }
28663             
28664         Roo.each(_this.toolbarItems, function(e) {
28665             _this.navgroup.addItem(e);
28666         });
28667         
28668         
28669         this.first = this.navgroup.addItem({
28670             tooltip: this.firstText,
28671             cls: "prev btn-outline-secondary",
28672             html : ' <i class="fa fa-step-backward"></i>',
28673             disabled: true,
28674             preventDefault: true,
28675             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28676         });
28677         
28678         this.prev =  this.navgroup.addItem({
28679             tooltip: this.prevText,
28680             cls: "prev btn-outline-secondary",
28681             html : ' <i class="fa fa-backward"></i>',
28682             disabled: true,
28683             preventDefault: true,
28684             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28685         });
28686     //this.addSeparator();
28687         
28688         
28689         var field = this.navgroup.addItem( {
28690             tagtype : 'span',
28691             cls : 'x-paging-position  btn-outline-secondary',
28692              disabled: true,
28693             html : this.beforePageText  +
28694                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28695                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28696          } ); //?? escaped?
28697         
28698         this.field = field.el.select('input', true).first();
28699         this.field.on("keydown", this.onPagingKeydown, this);
28700         this.field.on("focus", function(){this.dom.select();});
28701     
28702     
28703         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28704         //this.field.setHeight(18);
28705         //this.addSeparator();
28706         this.next = this.navgroup.addItem({
28707             tooltip: this.nextText,
28708             cls: "next btn-outline-secondary",
28709             html : ' <i class="fa fa-forward"></i>',
28710             disabled: true,
28711             preventDefault: true,
28712             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28713         });
28714         this.last = this.navgroup.addItem({
28715             tooltip: this.lastText,
28716             html : ' <i class="fa fa-step-forward"></i>',
28717             cls: "next btn-outline-secondary",
28718             disabled: true,
28719             preventDefault: true,
28720             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28721         });
28722     //this.addSeparator();
28723         this.loading = this.navgroup.addItem({
28724             tooltip: this.refreshText,
28725             cls: "btn-outline-secondary",
28726             html : ' <i class="fa fa-refresh"></i>',
28727             preventDefault: true,
28728             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28729         });
28730         
28731     },
28732
28733     // private
28734     updateInfo : function(){
28735         if(this.displayEl){
28736             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28737             var msg = count == 0 ?
28738                 this.emptyMsg :
28739                 String.format(
28740                     this.displayMsg,
28741                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28742                 );
28743             this.displayEl.update(msg);
28744         }
28745     },
28746
28747     // private
28748     onLoad : function(ds, r, o)
28749     {
28750         this.cursor = o.params && o.params.start ? o.params.start : 0;
28751         
28752         var d = this.getPageData(),
28753             ap = d.activePage,
28754             ps = d.pages;
28755         
28756         
28757         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28758         this.field.dom.value = ap;
28759         this.first.setDisabled(ap == 1);
28760         this.prev.setDisabled(ap == 1);
28761         this.next.setDisabled(ap == ps);
28762         this.last.setDisabled(ap == ps);
28763         this.loading.enable();
28764         this.updateInfo();
28765     },
28766
28767     // private
28768     getPageData : function(){
28769         var total = this.ds.getTotalCount();
28770         return {
28771             total : total,
28772             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28773             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28774         };
28775     },
28776
28777     // private
28778     onLoadError : function(){
28779         this.loading.enable();
28780     },
28781
28782     // private
28783     onPagingKeydown : function(e){
28784         var k = e.getKey();
28785         var d = this.getPageData();
28786         if(k == e.RETURN){
28787             var v = this.field.dom.value, pageNum;
28788             if(!v || isNaN(pageNum = parseInt(v, 10))){
28789                 this.field.dom.value = d.activePage;
28790                 return;
28791             }
28792             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28793             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28794             e.stopEvent();
28795         }
28796         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))
28797         {
28798           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28799           this.field.dom.value = pageNum;
28800           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28801           e.stopEvent();
28802         }
28803         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28804         {
28805           var v = this.field.dom.value, pageNum; 
28806           var increment = (e.shiftKey) ? 10 : 1;
28807           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28808                 increment *= -1;
28809           }
28810           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28811             this.field.dom.value = d.activePage;
28812             return;
28813           }
28814           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28815           {
28816             this.field.dom.value = parseInt(v, 10) + increment;
28817             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28818             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28819           }
28820           e.stopEvent();
28821         }
28822     },
28823
28824     // private
28825     beforeLoad : function(){
28826         if(this.loading){
28827             this.loading.disable();
28828         }
28829     },
28830
28831     // private
28832     onClick : function(which){
28833         
28834         var ds = this.ds;
28835         if (!ds) {
28836             return;
28837         }
28838         
28839         switch(which){
28840             case "first":
28841                 ds.load({params:{start: 0, limit: this.pageSize}});
28842             break;
28843             case "prev":
28844                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28845             break;
28846             case "next":
28847                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28848             break;
28849             case "last":
28850                 var total = ds.getTotalCount();
28851                 var extra = total % this.pageSize;
28852                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28853                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28854             break;
28855             case "refresh":
28856                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28857             break;
28858         }
28859     },
28860
28861     /**
28862      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28863      * @param {Roo.data.Store} store The data store to unbind
28864      */
28865     unbind : function(ds){
28866         ds.un("beforeload", this.beforeLoad, this);
28867         ds.un("load", this.onLoad, this);
28868         ds.un("loadexception", this.onLoadError, this);
28869         ds.un("remove", this.updateInfo, this);
28870         ds.un("add", this.updateInfo, this);
28871         this.ds = undefined;
28872     },
28873
28874     /**
28875      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28876      * @param {Roo.data.Store} store The data store to bind
28877      */
28878     bind : function(ds){
28879         ds.on("beforeload", this.beforeLoad, this);
28880         ds.on("load", this.onLoad, this);
28881         ds.on("loadexception", this.onLoadError, this);
28882         ds.on("remove", this.updateInfo, this);
28883         ds.on("add", this.updateInfo, this);
28884         this.ds = ds;
28885     }
28886 });/*
28887  * - LGPL
28888  *
28889  * element
28890  * 
28891  */
28892
28893 /**
28894  * @class Roo.bootstrap.MessageBar
28895  * @extends Roo.bootstrap.Component
28896  * Bootstrap MessageBar class
28897  * @cfg {String} html contents of the MessageBar
28898  * @cfg {String} weight (info | success | warning | danger) default info
28899  * @cfg {String} beforeClass insert the bar before the given class
28900  * @cfg {Boolean} closable (true | false) default false
28901  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28902  * 
28903  * @constructor
28904  * Create a new Element
28905  * @param {Object} config The config object
28906  */
28907
28908 Roo.bootstrap.MessageBar = function(config){
28909     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28910 };
28911
28912 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28913     
28914     html: '',
28915     weight: 'info',
28916     closable: false,
28917     fixed: false,
28918     beforeClass: 'bootstrap-sticky-wrap',
28919     
28920     getAutoCreate : function(){
28921         
28922         var cfg = {
28923             tag: 'div',
28924             cls: 'alert alert-dismissable alert-' + this.weight,
28925             cn: [
28926                 {
28927                     tag: 'span',
28928                     cls: 'message',
28929                     html: this.html || ''
28930                 }
28931             ]
28932         };
28933         
28934         if(this.fixed){
28935             cfg.cls += ' alert-messages-fixed';
28936         }
28937         
28938         if(this.closable){
28939             cfg.cn.push({
28940                 tag: 'button',
28941                 cls: 'close',
28942                 html: 'x'
28943             });
28944         }
28945         
28946         return cfg;
28947     },
28948     
28949     onRender : function(ct, position)
28950     {
28951         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28952         
28953         if(!this.el){
28954             var cfg = Roo.apply({},  this.getAutoCreate());
28955             cfg.id = Roo.id();
28956             
28957             if (this.cls) {
28958                 cfg.cls += ' ' + this.cls;
28959             }
28960             if (this.style) {
28961                 cfg.style = this.style;
28962             }
28963             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28964             
28965             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28966         }
28967         
28968         this.el.select('>button.close').on('click', this.hide, this);
28969         
28970     },
28971     
28972     show : function()
28973     {
28974         if (!this.rendered) {
28975             this.render();
28976         }
28977         
28978         this.el.show();
28979         
28980         this.fireEvent('show', this);
28981         
28982     },
28983     
28984     hide : function()
28985     {
28986         if (!this.rendered) {
28987             this.render();
28988         }
28989         
28990         this.el.hide();
28991         
28992         this.fireEvent('hide', this);
28993     },
28994     
28995     update : function()
28996     {
28997 //        var e = this.el.dom.firstChild;
28998 //        
28999 //        if(this.closable){
29000 //            e = e.nextSibling;
29001 //        }
29002 //        
29003 //        e.data = this.html || '';
29004
29005         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29006     }
29007    
29008 });
29009
29010  
29011
29012      /*
29013  * - LGPL
29014  *
29015  * Graph
29016  * 
29017  */
29018
29019
29020 /**
29021  * @class Roo.bootstrap.Graph
29022  * @extends Roo.bootstrap.Component
29023  * Bootstrap Graph class
29024 > Prameters
29025  -sm {number} sm 4
29026  -md {number} md 5
29027  @cfg {String} graphtype  bar | vbar | pie
29028  @cfg {number} g_x coodinator | centre x (pie)
29029  @cfg {number} g_y coodinator | centre y (pie)
29030  @cfg {number} g_r radius (pie)
29031  @cfg {number} g_height height of the chart (respected by all elements in the set)
29032  @cfg {number} g_width width of the chart (respected by all elements in the set)
29033  @cfg {Object} title The title of the chart
29034     
29035  -{Array}  values
29036  -opts (object) options for the chart 
29037      o {
29038      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29039      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29040      o vgutter (number)
29041      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.
29042      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29043      o to
29044      o stretch (boolean)
29045      o }
29046  -opts (object) options for the pie
29047      o{
29048      o cut
29049      o startAngle (number)
29050      o endAngle (number)
29051      } 
29052  *
29053  * @constructor
29054  * Create a new Input
29055  * @param {Object} config The config object
29056  */
29057
29058 Roo.bootstrap.Graph = function(config){
29059     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29060     
29061     this.addEvents({
29062         // img events
29063         /**
29064          * @event click
29065          * The img click event for the img.
29066          * @param {Roo.EventObject} e
29067          */
29068         "click" : true
29069     });
29070 };
29071
29072 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29073     
29074     sm: 4,
29075     md: 5,
29076     graphtype: 'bar',
29077     g_height: 250,
29078     g_width: 400,
29079     g_x: 50,
29080     g_y: 50,
29081     g_r: 30,
29082     opts:{
29083         //g_colors: this.colors,
29084         g_type: 'soft',
29085         g_gutter: '20%'
29086
29087     },
29088     title : false,
29089
29090     getAutoCreate : function(){
29091         
29092         var cfg = {
29093             tag: 'div',
29094             html : null
29095         };
29096         
29097         
29098         return  cfg;
29099     },
29100
29101     onRender : function(ct,position){
29102         
29103         
29104         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29105         
29106         if (typeof(Raphael) == 'undefined') {
29107             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29108             return;
29109         }
29110         
29111         this.raphael = Raphael(this.el.dom);
29112         
29113                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29114                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29115                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29116                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29117                 /*
29118                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29119                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29120                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29121                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29122                 
29123                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29124                 r.barchart(330, 10, 300, 220, data1);
29125                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29126                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29127                 */
29128                 
29129                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29130                 // r.barchart(30, 30, 560, 250,  xdata, {
29131                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29132                 //     axis : "0 0 1 1",
29133                 //     axisxlabels :  xdata
29134                 //     //yvalues : cols,
29135                    
29136                 // });
29137 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29138 //        
29139 //        this.load(null,xdata,{
29140 //                axis : "0 0 1 1",
29141 //                axisxlabels :  xdata
29142 //                });
29143
29144     },
29145
29146     load : function(graphtype,xdata,opts)
29147     {
29148         this.raphael.clear();
29149         if(!graphtype) {
29150             graphtype = this.graphtype;
29151         }
29152         if(!opts){
29153             opts = this.opts;
29154         }
29155         var r = this.raphael,
29156             fin = function () {
29157                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29158             },
29159             fout = function () {
29160                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29161             },
29162             pfin = function() {
29163                 this.sector.stop();
29164                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29165
29166                 if (this.label) {
29167                     this.label[0].stop();
29168                     this.label[0].attr({ r: 7.5 });
29169                     this.label[1].attr({ "font-weight": 800 });
29170                 }
29171             },
29172             pfout = function() {
29173                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29174
29175                 if (this.label) {
29176                     this.label[0].animate({ r: 5 }, 500, "bounce");
29177                     this.label[1].attr({ "font-weight": 400 });
29178                 }
29179             };
29180
29181         switch(graphtype){
29182             case 'bar':
29183                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29184                 break;
29185             case 'hbar':
29186                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29187                 break;
29188             case 'pie':
29189 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29190 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29191 //            
29192                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29193                 
29194                 break;
29195
29196         }
29197         
29198         if(this.title){
29199             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29200         }
29201         
29202     },
29203     
29204     setTitle: function(o)
29205     {
29206         this.title = o;
29207     },
29208     
29209     initEvents: function() {
29210         
29211         if(!this.href){
29212             this.el.on('click', this.onClick, this);
29213         }
29214     },
29215     
29216     onClick : function(e)
29217     {
29218         Roo.log('img onclick');
29219         this.fireEvent('click', this, e);
29220     }
29221    
29222 });
29223
29224  
29225 /*
29226  * - LGPL
29227  *
29228  * numberBox
29229  * 
29230  */
29231 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29232
29233 /**
29234  * @class Roo.bootstrap.dash.NumberBox
29235  * @extends Roo.bootstrap.Component
29236  * Bootstrap NumberBox class
29237  * @cfg {String} headline Box headline
29238  * @cfg {String} content Box content
29239  * @cfg {String} icon Box icon
29240  * @cfg {String} footer Footer text
29241  * @cfg {String} fhref Footer href
29242  * 
29243  * @constructor
29244  * Create a new NumberBox
29245  * @param {Object} config The config object
29246  */
29247
29248
29249 Roo.bootstrap.dash.NumberBox = function(config){
29250     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29251     
29252 };
29253
29254 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29255     
29256     headline : '',
29257     content : '',
29258     icon : '',
29259     footer : '',
29260     fhref : '',
29261     ficon : '',
29262     
29263     getAutoCreate : function(){
29264         
29265         var cfg = {
29266             tag : 'div',
29267             cls : 'small-box ',
29268             cn : [
29269                 {
29270                     tag : 'div',
29271                     cls : 'inner',
29272                     cn :[
29273                         {
29274                             tag : 'h3',
29275                             cls : 'roo-headline',
29276                             html : this.headline
29277                         },
29278                         {
29279                             tag : 'p',
29280                             cls : 'roo-content',
29281                             html : this.content
29282                         }
29283                     ]
29284                 }
29285             ]
29286         };
29287         
29288         if(this.icon){
29289             cfg.cn.push({
29290                 tag : 'div',
29291                 cls : 'icon',
29292                 cn :[
29293                     {
29294                         tag : 'i',
29295                         cls : 'ion ' + this.icon
29296                     }
29297                 ]
29298             });
29299         }
29300         
29301         if(this.footer){
29302             var footer = {
29303                 tag : 'a',
29304                 cls : 'small-box-footer',
29305                 href : this.fhref || '#',
29306                 html : this.footer
29307             };
29308             
29309             cfg.cn.push(footer);
29310             
29311         }
29312         
29313         return  cfg;
29314     },
29315
29316     onRender : function(ct,position){
29317         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29318
29319
29320        
29321                 
29322     },
29323
29324     setHeadline: function (value)
29325     {
29326         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29327     },
29328     
29329     setFooter: function (value, href)
29330     {
29331         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29332         
29333         if(href){
29334             this.el.select('a.small-box-footer',true).first().attr('href', href);
29335         }
29336         
29337     },
29338
29339     setContent: function (value)
29340     {
29341         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29342     },
29343
29344     initEvents: function() 
29345     {   
29346         
29347     }
29348     
29349 });
29350
29351  
29352 /*
29353  * - LGPL
29354  *
29355  * TabBox
29356  * 
29357  */
29358 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29359
29360 /**
29361  * @class Roo.bootstrap.dash.TabBox
29362  * @extends Roo.bootstrap.Component
29363  * @children Roo.bootstrap.dash.TabPane
29364  * Bootstrap TabBox class
29365  * @cfg {String} title Title of the TabBox
29366  * @cfg {String} icon Icon of the TabBox
29367  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29368  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29369  * 
29370  * @constructor
29371  * Create a new TabBox
29372  * @param {Object} config The config object
29373  */
29374
29375
29376 Roo.bootstrap.dash.TabBox = function(config){
29377     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29378     this.addEvents({
29379         // raw events
29380         /**
29381          * @event addpane
29382          * When a pane is added
29383          * @param {Roo.bootstrap.dash.TabPane} pane
29384          */
29385         "addpane" : true,
29386         /**
29387          * @event activatepane
29388          * When a pane is activated
29389          * @param {Roo.bootstrap.dash.TabPane} pane
29390          */
29391         "activatepane" : true
29392         
29393          
29394     });
29395     
29396     this.panes = [];
29397 };
29398
29399 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29400
29401     title : '',
29402     icon : false,
29403     showtabs : true,
29404     tabScrollable : false,
29405     
29406     getChildContainer : function()
29407     {
29408         return this.el.select('.tab-content', true).first();
29409     },
29410     
29411     getAutoCreate : function(){
29412         
29413         var header = {
29414             tag: 'li',
29415             cls: 'pull-left header',
29416             html: this.title,
29417             cn : []
29418         };
29419         
29420         if(this.icon){
29421             header.cn.push({
29422                 tag: 'i',
29423                 cls: 'fa ' + this.icon
29424             });
29425         }
29426         
29427         var h = {
29428             tag: 'ul',
29429             cls: 'nav nav-tabs pull-right',
29430             cn: [
29431                 header
29432             ]
29433         };
29434         
29435         if(this.tabScrollable){
29436             h = {
29437                 tag: 'div',
29438                 cls: 'tab-header',
29439                 cn: [
29440                     {
29441                         tag: 'ul',
29442                         cls: 'nav nav-tabs pull-right',
29443                         cn: [
29444                             header
29445                         ]
29446                     }
29447                 ]
29448             };
29449         }
29450         
29451         var cfg = {
29452             tag: 'div',
29453             cls: 'nav-tabs-custom',
29454             cn: [
29455                 h,
29456                 {
29457                     tag: 'div',
29458                     cls: 'tab-content no-padding',
29459                     cn: []
29460                 }
29461             ]
29462         };
29463
29464         return  cfg;
29465     },
29466     initEvents : function()
29467     {
29468         //Roo.log('add add pane handler');
29469         this.on('addpane', this.onAddPane, this);
29470     },
29471      /**
29472      * Updates the box title
29473      * @param {String} html to set the title to.
29474      */
29475     setTitle : function(value)
29476     {
29477         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29478     },
29479     onAddPane : function(pane)
29480     {
29481         this.panes.push(pane);
29482         //Roo.log('addpane');
29483         //Roo.log(pane);
29484         // tabs are rendere left to right..
29485         if(!this.showtabs){
29486             return;
29487         }
29488         
29489         var ctr = this.el.select('.nav-tabs', true).first();
29490          
29491          
29492         var existing = ctr.select('.nav-tab',true);
29493         var qty = existing.getCount();;
29494         
29495         
29496         var tab = ctr.createChild({
29497             tag : 'li',
29498             cls : 'nav-tab' + (qty ? '' : ' active'),
29499             cn : [
29500                 {
29501                     tag : 'a',
29502                     href:'#',
29503                     html : pane.title
29504                 }
29505             ]
29506         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29507         pane.tab = tab;
29508         
29509         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29510         if (!qty) {
29511             pane.el.addClass('active');
29512         }
29513         
29514                 
29515     },
29516     onTabClick : function(ev,un,ob,pane)
29517     {
29518         //Roo.log('tab - prev default');
29519         ev.preventDefault();
29520         
29521         
29522         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29523         pane.tab.addClass('active');
29524         //Roo.log(pane.title);
29525         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29526         // technically we should have a deactivate event.. but maybe add later.
29527         // and it should not de-activate the selected tab...
29528         this.fireEvent('activatepane', pane);
29529         pane.el.addClass('active');
29530         pane.fireEvent('activate');
29531         
29532         
29533     },
29534     
29535     getActivePane : function()
29536     {
29537         var r = false;
29538         Roo.each(this.panes, function(p) {
29539             if(p.el.hasClass('active')){
29540                 r = p;
29541                 return false;
29542             }
29543             
29544             return;
29545         });
29546         
29547         return r;
29548     }
29549     
29550     
29551 });
29552
29553  
29554 /*
29555  * - LGPL
29556  *
29557  * Tab pane
29558  * 
29559  */
29560 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29561 /**
29562  * @class Roo.bootstrap.TabPane
29563  * @extends Roo.bootstrap.Component
29564  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29565  * Bootstrap TabPane class
29566  * @cfg {Boolean} active (false | true) Default false
29567  * @cfg {String} title title of panel
29568
29569  * 
29570  * @constructor
29571  * Create a new TabPane
29572  * @param {Object} config The config object
29573  */
29574
29575 Roo.bootstrap.dash.TabPane = function(config){
29576     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29577     
29578     this.addEvents({
29579         // raw events
29580         /**
29581          * @event activate
29582          * When a pane is activated
29583          * @param {Roo.bootstrap.dash.TabPane} pane
29584          */
29585         "activate" : true
29586          
29587     });
29588 };
29589
29590 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29591     
29592     active : false,
29593     title : '',
29594     
29595     // the tabBox that this is attached to.
29596     tab : false,
29597      
29598     getAutoCreate : function() 
29599     {
29600         var cfg = {
29601             tag: 'div',
29602             cls: 'tab-pane'
29603         };
29604         
29605         if(this.active){
29606             cfg.cls += ' active';
29607         }
29608         
29609         return cfg;
29610     },
29611     initEvents  : function()
29612     {
29613         //Roo.log('trigger add pane handler');
29614         this.parent().fireEvent('addpane', this)
29615     },
29616     
29617      /**
29618      * Updates the tab title 
29619      * @param {String} html to set the title to.
29620      */
29621     setTitle: function(str)
29622     {
29623         if (!this.tab) {
29624             return;
29625         }
29626         this.title = str;
29627         this.tab.select('a', true).first().dom.innerHTML = str;
29628         
29629     }
29630     
29631     
29632     
29633 });
29634
29635  
29636
29637
29638  /*
29639  * - LGPL
29640  *
29641  * Tooltip
29642  * 
29643  */
29644
29645 /**
29646  * @class Roo.bootstrap.Tooltip
29647  * Bootstrap Tooltip class
29648  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29649  * to determine which dom element triggers the tooltip.
29650  * 
29651  * It needs to add support for additional attributes like tooltip-position
29652  * 
29653  * @constructor
29654  * Create a new Toolti
29655  * @param {Object} config The config object
29656  */
29657
29658 Roo.bootstrap.Tooltip = function(config){
29659     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29660     
29661     this.alignment = Roo.bootstrap.Tooltip.alignment;
29662     
29663     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29664         this.alignment = config.alignment;
29665     }
29666     
29667 };
29668
29669 Roo.apply(Roo.bootstrap.Tooltip, {
29670     /**
29671      * @function init initialize tooltip monitoring.
29672      * @static
29673      */
29674     currentEl : false,
29675     currentTip : false,
29676     currentRegion : false,
29677     
29678     //  init : delay?
29679     
29680     init : function()
29681     {
29682         Roo.get(document).on('mouseover', this.enter ,this);
29683         Roo.get(document).on('mouseout', this.leave, this);
29684          
29685         
29686         this.currentTip = new Roo.bootstrap.Tooltip();
29687     },
29688     
29689     enter : function(ev)
29690     {
29691         var dom = ev.getTarget();
29692         
29693         //Roo.log(['enter',dom]);
29694         var el = Roo.fly(dom);
29695         if (this.currentEl) {
29696             //Roo.log(dom);
29697             //Roo.log(this.currentEl);
29698             //Roo.log(this.currentEl.contains(dom));
29699             if (this.currentEl == el) {
29700                 return;
29701             }
29702             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29703                 return;
29704             }
29705
29706         }
29707         
29708         if (this.currentTip.el) {
29709             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29710         }    
29711         //Roo.log(ev);
29712         
29713         if(!el || el.dom == document){
29714             return;
29715         }
29716         
29717         var bindEl = el; 
29718         var pel = false;
29719         if (!el.attr('tooltip')) {
29720             pel = el.findParent("[tooltip]");
29721             if (pel) {
29722                 bindEl = Roo.get(pel);
29723             }
29724         }
29725         
29726        
29727         
29728         // you can not look for children, as if el is the body.. then everythign is the child..
29729         if (!pel && !el.attr('tooltip')) { //
29730             if (!el.select("[tooltip]").elements.length) {
29731                 return;
29732             }
29733             // is the mouse over this child...?
29734             bindEl = el.select("[tooltip]").first();
29735             var xy = ev.getXY();
29736             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29737                 //Roo.log("not in region.");
29738                 return;
29739             }
29740             //Roo.log("child element over..");
29741             
29742         }
29743         this.currentEl = el;
29744         this.currentTip.bind(bindEl);
29745         this.currentRegion = Roo.lib.Region.getRegion(dom);
29746         this.currentTip.enter();
29747         
29748     },
29749     leave : function(ev)
29750     {
29751         var dom = ev.getTarget();
29752         //Roo.log(['leave',dom]);
29753         if (!this.currentEl) {
29754             return;
29755         }
29756         
29757         
29758         if (dom != this.currentEl.dom) {
29759             return;
29760         }
29761         var xy = ev.getXY();
29762         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29763             return;
29764         }
29765         // only activate leave if mouse cursor is outside... bounding box..
29766         
29767         
29768         
29769         
29770         if (this.currentTip) {
29771             this.currentTip.leave();
29772         }
29773         //Roo.log('clear currentEl');
29774         this.currentEl = false;
29775         
29776         
29777     },
29778     alignment : {
29779         'left' : ['r-l', [-2,0], 'right'],
29780         'right' : ['l-r', [2,0], 'left'],
29781         'bottom' : ['t-b', [0,2], 'top'],
29782         'top' : [ 'b-t', [0,-2], 'bottom']
29783     }
29784     
29785 });
29786
29787
29788 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29789     
29790     
29791     bindEl : false,
29792     
29793     delay : null, // can be { show : 300 , hide: 500}
29794     
29795     timeout : null,
29796     
29797     hoverState : null, //???
29798     
29799     placement : 'bottom', 
29800     
29801     alignment : false,
29802     
29803     getAutoCreate : function(){
29804     
29805         var cfg = {
29806            cls : 'tooltip',   
29807            role : 'tooltip',
29808            cn : [
29809                 {
29810                     cls : 'tooltip-arrow arrow'
29811                 },
29812                 {
29813                     cls : 'tooltip-inner'
29814                 }
29815            ]
29816         };
29817         
29818         return cfg;
29819     },
29820     bind : function(el)
29821     {
29822         this.bindEl = el;
29823     },
29824     
29825     initEvents : function()
29826     {
29827         this.arrowEl = this.el.select('.arrow', true).first();
29828         this.innerEl = this.el.select('.tooltip-inner', true).first();
29829     },
29830     
29831     enter : function () {
29832        
29833         if (this.timeout != null) {
29834             clearTimeout(this.timeout);
29835         }
29836         
29837         this.hoverState = 'in';
29838          //Roo.log("enter - show");
29839         if (!this.delay || !this.delay.show) {
29840             this.show();
29841             return;
29842         }
29843         var _t = this;
29844         this.timeout = setTimeout(function () {
29845             if (_t.hoverState == 'in') {
29846                 _t.show();
29847             }
29848         }, this.delay.show);
29849     },
29850     leave : function()
29851     {
29852         clearTimeout(this.timeout);
29853     
29854         this.hoverState = 'out';
29855          if (!this.delay || !this.delay.hide) {
29856             this.hide();
29857             return;
29858         }
29859        
29860         var _t = this;
29861         this.timeout = setTimeout(function () {
29862             //Roo.log("leave - timeout");
29863             
29864             if (_t.hoverState == 'out') {
29865                 _t.hide();
29866                 Roo.bootstrap.Tooltip.currentEl = false;
29867             }
29868         }, delay);
29869     },
29870     
29871     show : function (msg)
29872     {
29873         if (!this.el) {
29874             this.render(document.body);
29875         }
29876         // set content.
29877         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29878         
29879         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29880         
29881         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29882         
29883         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29884                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29885         
29886         var placement = typeof this.placement == 'function' ?
29887             this.placement.call(this, this.el, on_el) :
29888             this.placement;
29889             
29890         var autoToken = /\s?auto?\s?/i;
29891         var autoPlace = autoToken.test(placement);
29892         if (autoPlace) {
29893             placement = placement.replace(autoToken, '') || 'top';
29894         }
29895         
29896         //this.el.detach()
29897         //this.el.setXY([0,0]);
29898         this.el.show();
29899         //this.el.dom.style.display='block';
29900         
29901         //this.el.appendTo(on_el);
29902         
29903         var p = this.getPosition();
29904         var box = this.el.getBox();
29905         
29906         if (autoPlace) {
29907             // fixme..
29908         }
29909         
29910         var align = this.alignment[placement];
29911         
29912         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29913         
29914         if(placement == 'top' || placement == 'bottom'){
29915             if(xy[0] < 0){
29916                 placement = 'right';
29917             }
29918             
29919             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29920                 placement = 'left';
29921             }
29922             
29923             var scroll = Roo.select('body', true).first().getScroll();
29924             
29925             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29926                 placement = 'top';
29927             }
29928             
29929             align = this.alignment[placement];
29930             
29931             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29932             
29933         }
29934         
29935         var elems = document.getElementsByTagName('div');
29936         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29937         for (var i = 0; i < elems.length; i++) {
29938           var zindex = Number.parseInt(
29939                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29940                 10
29941           );
29942           if (zindex > highest) {
29943             highest = zindex;
29944           }
29945         }
29946         
29947         
29948         
29949         this.el.dom.style.zIndex = highest;
29950         
29951         this.el.alignTo(this.bindEl, align[0],align[1]);
29952         //var arrow = this.el.select('.arrow',true).first();
29953         //arrow.set(align[2], 
29954         
29955         this.el.addClass(placement);
29956         this.el.addClass("bs-tooltip-"+ placement);
29957         
29958         this.el.addClass('in fade show');
29959         
29960         this.hoverState = null;
29961         
29962         if (this.el.hasClass('fade')) {
29963             // fade it?
29964         }
29965         
29966         
29967         
29968         
29969         
29970     },
29971     hide : function()
29972     {
29973          
29974         if (!this.el) {
29975             return;
29976         }
29977         //this.el.setXY([0,0]);
29978         this.el.removeClass(['show', 'in']);
29979         //this.el.hide();
29980         
29981     }
29982     
29983 });
29984  
29985
29986  /*
29987  * - LGPL
29988  *
29989  * Location Picker
29990  * 
29991  */
29992
29993 /**
29994  * @class Roo.bootstrap.LocationPicker
29995  * @extends Roo.bootstrap.Component
29996  * Bootstrap LocationPicker class
29997  * @cfg {Number} latitude Position when init default 0
29998  * @cfg {Number} longitude Position when init default 0
29999  * @cfg {Number} zoom default 15
30000  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30001  * @cfg {Boolean} mapTypeControl default false
30002  * @cfg {Boolean} disableDoubleClickZoom default false
30003  * @cfg {Boolean} scrollwheel default true
30004  * @cfg {Boolean} streetViewControl default false
30005  * @cfg {Number} radius default 0
30006  * @cfg {String} locationName
30007  * @cfg {Boolean} draggable default true
30008  * @cfg {Boolean} enableAutocomplete default false
30009  * @cfg {Boolean} enableReverseGeocode default true
30010  * @cfg {String} markerTitle
30011  * 
30012  * @constructor
30013  * Create a new LocationPicker
30014  * @param {Object} config The config object
30015  */
30016
30017
30018 Roo.bootstrap.LocationPicker = function(config){
30019     
30020     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30021     
30022     this.addEvents({
30023         /**
30024          * @event initial
30025          * Fires when the picker initialized.
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          * @param {Google Location} location
30028          */
30029         initial : true,
30030         /**
30031          * @event positionchanged
30032          * Fires when the picker position changed.
30033          * @param {Roo.bootstrap.LocationPicker} this
30034          * @param {Google Location} location
30035          */
30036         positionchanged : true,
30037         /**
30038          * @event resize
30039          * Fires when the map resize.
30040          * @param {Roo.bootstrap.LocationPicker} this
30041          */
30042         resize : true,
30043         /**
30044          * @event show
30045          * Fires when the map show.
30046          * @param {Roo.bootstrap.LocationPicker} this
30047          */
30048         show : true,
30049         /**
30050          * @event hide
30051          * Fires when the map hide.
30052          * @param {Roo.bootstrap.LocationPicker} this
30053          */
30054         hide : true,
30055         /**
30056          * @event mapClick
30057          * Fires when click the map.
30058          * @param {Roo.bootstrap.LocationPicker} this
30059          * @param {Map event} e
30060          */
30061         mapClick : true,
30062         /**
30063          * @event mapRightClick
30064          * Fires when right click the map.
30065          * @param {Roo.bootstrap.LocationPicker} this
30066          * @param {Map event} e
30067          */
30068         mapRightClick : true,
30069         /**
30070          * @event markerClick
30071          * Fires when click the marker.
30072          * @param {Roo.bootstrap.LocationPicker} this
30073          * @param {Map event} e
30074          */
30075         markerClick : true,
30076         /**
30077          * @event markerRightClick
30078          * Fires when right click the marker.
30079          * @param {Roo.bootstrap.LocationPicker} this
30080          * @param {Map event} e
30081          */
30082         markerRightClick : true,
30083         /**
30084          * @event OverlayViewDraw
30085          * Fires when OverlayView Draw
30086          * @param {Roo.bootstrap.LocationPicker} this
30087          */
30088         OverlayViewDraw : true,
30089         /**
30090          * @event OverlayViewOnAdd
30091          * Fires when OverlayView Draw
30092          * @param {Roo.bootstrap.LocationPicker} this
30093          */
30094         OverlayViewOnAdd : true,
30095         /**
30096          * @event OverlayViewOnRemove
30097          * Fires when OverlayView Draw
30098          * @param {Roo.bootstrap.LocationPicker} this
30099          */
30100         OverlayViewOnRemove : true,
30101         /**
30102          * @event OverlayViewShow
30103          * Fires when OverlayView Draw
30104          * @param {Roo.bootstrap.LocationPicker} this
30105          * @param {Pixel} cpx
30106          */
30107         OverlayViewShow : true,
30108         /**
30109          * @event OverlayViewHide
30110          * Fires when OverlayView Draw
30111          * @param {Roo.bootstrap.LocationPicker} this
30112          */
30113         OverlayViewHide : true,
30114         /**
30115          * @event loadexception
30116          * Fires when load google lib failed.
30117          * @param {Roo.bootstrap.LocationPicker} this
30118          */
30119         loadexception : true
30120     });
30121         
30122 };
30123
30124 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30125     
30126     gMapContext: false,
30127     
30128     latitude: 0,
30129     longitude: 0,
30130     zoom: 15,
30131     mapTypeId: false,
30132     mapTypeControl: false,
30133     disableDoubleClickZoom: false,
30134     scrollwheel: true,
30135     streetViewControl: false,
30136     radius: 0,
30137     locationName: '',
30138     draggable: true,
30139     enableAutocomplete: false,
30140     enableReverseGeocode: true,
30141     markerTitle: '',
30142     
30143     getAutoCreate: function()
30144     {
30145
30146         var cfg = {
30147             tag: 'div',
30148             cls: 'roo-location-picker'
30149         };
30150         
30151         return cfg
30152     },
30153     
30154     initEvents: function(ct, position)
30155     {       
30156         if(!this.el.getWidth() || this.isApplied()){
30157             return;
30158         }
30159         
30160         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30161         
30162         this.initial();
30163     },
30164     
30165     initial: function()
30166     {
30167         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30168             this.fireEvent('loadexception', this);
30169             return;
30170         }
30171         
30172         if(!this.mapTypeId){
30173             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30174         }
30175         
30176         this.gMapContext = this.GMapContext();
30177         
30178         this.initOverlayView();
30179         
30180         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30181         
30182         var _this = this;
30183                 
30184         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30185             _this.setPosition(_this.gMapContext.marker.position);
30186         });
30187         
30188         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30189             _this.fireEvent('mapClick', this, event);
30190             
30191         });
30192
30193         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30194             _this.fireEvent('mapRightClick', this, event);
30195             
30196         });
30197         
30198         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30199             _this.fireEvent('markerClick', this, event);
30200             
30201         });
30202
30203         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30204             _this.fireEvent('markerRightClick', this, event);
30205             
30206         });
30207         
30208         this.setPosition(this.gMapContext.location);
30209         
30210         this.fireEvent('initial', this, this.gMapContext.location);
30211     },
30212     
30213     initOverlayView: function()
30214     {
30215         var _this = this;
30216         
30217         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30218             
30219             draw: function()
30220             {
30221                 _this.fireEvent('OverlayViewDraw', _this);
30222             },
30223             
30224             onAdd: function()
30225             {
30226                 _this.fireEvent('OverlayViewOnAdd', _this);
30227             },
30228             
30229             onRemove: function()
30230             {
30231                 _this.fireEvent('OverlayViewOnRemove', _this);
30232             },
30233             
30234             show: function(cpx)
30235             {
30236                 _this.fireEvent('OverlayViewShow', _this, cpx);
30237             },
30238             
30239             hide: function()
30240             {
30241                 _this.fireEvent('OverlayViewHide', _this);
30242             }
30243             
30244         });
30245     },
30246     
30247     fromLatLngToContainerPixel: function(event)
30248     {
30249         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30250     },
30251     
30252     isApplied: function() 
30253     {
30254         return this.getGmapContext() == false ? false : true;
30255     },
30256     
30257     getGmapContext: function() 
30258     {
30259         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30260     },
30261     
30262     GMapContext: function() 
30263     {
30264         var position = new google.maps.LatLng(this.latitude, this.longitude);
30265         
30266         var _map = new google.maps.Map(this.el.dom, {
30267             center: position,
30268             zoom: this.zoom,
30269             mapTypeId: this.mapTypeId,
30270             mapTypeControl: this.mapTypeControl,
30271             disableDoubleClickZoom: this.disableDoubleClickZoom,
30272             scrollwheel: this.scrollwheel,
30273             streetViewControl: this.streetViewControl,
30274             locationName: this.locationName,
30275             draggable: this.draggable,
30276             enableAutocomplete: this.enableAutocomplete,
30277             enableReverseGeocode: this.enableReverseGeocode
30278         });
30279         
30280         var _marker = new google.maps.Marker({
30281             position: position,
30282             map: _map,
30283             title: this.markerTitle,
30284             draggable: this.draggable
30285         });
30286         
30287         return {
30288             map: _map,
30289             marker: _marker,
30290             circle: null,
30291             location: position,
30292             radius: this.radius,
30293             locationName: this.locationName,
30294             addressComponents: {
30295                 formatted_address: null,
30296                 addressLine1: null,
30297                 addressLine2: null,
30298                 streetName: null,
30299                 streetNumber: null,
30300                 city: null,
30301                 district: null,
30302                 state: null,
30303                 stateOrProvince: null
30304             },
30305             settings: this,
30306             domContainer: this.el.dom,
30307             geodecoder: new google.maps.Geocoder()
30308         };
30309     },
30310     
30311     drawCircle: function(center, radius, options) 
30312     {
30313         if (this.gMapContext.circle != null) {
30314             this.gMapContext.circle.setMap(null);
30315         }
30316         if (radius > 0) {
30317             radius *= 1;
30318             options = Roo.apply({}, options, {
30319                 strokeColor: "#0000FF",
30320                 strokeOpacity: .35,
30321                 strokeWeight: 2,
30322                 fillColor: "#0000FF",
30323                 fillOpacity: .2
30324             });
30325             
30326             options.map = this.gMapContext.map;
30327             options.radius = radius;
30328             options.center = center;
30329             this.gMapContext.circle = new google.maps.Circle(options);
30330             return this.gMapContext.circle;
30331         }
30332         
30333         return null;
30334     },
30335     
30336     setPosition: function(location) 
30337     {
30338         this.gMapContext.location = location;
30339         this.gMapContext.marker.setPosition(location);
30340         this.gMapContext.map.panTo(location);
30341         this.drawCircle(location, this.gMapContext.radius, {});
30342         
30343         var _this = this;
30344         
30345         if (this.gMapContext.settings.enableReverseGeocode) {
30346             this.gMapContext.geodecoder.geocode({
30347                 latLng: this.gMapContext.location
30348             }, function(results, status) {
30349                 
30350                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30351                     _this.gMapContext.locationName = results[0].formatted_address;
30352                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30353                     
30354                     _this.fireEvent('positionchanged', this, location);
30355                 }
30356             });
30357             
30358             return;
30359         }
30360         
30361         this.fireEvent('positionchanged', this, location);
30362     },
30363     
30364     resize: function()
30365     {
30366         google.maps.event.trigger(this.gMapContext.map, "resize");
30367         
30368         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30369         
30370         this.fireEvent('resize', this);
30371     },
30372     
30373     setPositionByLatLng: function(latitude, longitude)
30374     {
30375         this.setPosition(new google.maps.LatLng(latitude, longitude));
30376     },
30377     
30378     getCurrentPosition: function() 
30379     {
30380         return {
30381             latitude: this.gMapContext.location.lat(),
30382             longitude: this.gMapContext.location.lng()
30383         };
30384     },
30385     
30386     getAddressName: function() 
30387     {
30388         return this.gMapContext.locationName;
30389     },
30390     
30391     getAddressComponents: function() 
30392     {
30393         return this.gMapContext.addressComponents;
30394     },
30395     
30396     address_component_from_google_geocode: function(address_components) 
30397     {
30398         var result = {};
30399         
30400         for (var i = 0; i < address_components.length; i++) {
30401             var component = address_components[i];
30402             if (component.types.indexOf("postal_code") >= 0) {
30403                 result.postalCode = component.short_name;
30404             } else if (component.types.indexOf("street_number") >= 0) {
30405                 result.streetNumber = component.short_name;
30406             } else if (component.types.indexOf("route") >= 0) {
30407                 result.streetName = component.short_name;
30408             } else if (component.types.indexOf("neighborhood") >= 0) {
30409                 result.city = component.short_name;
30410             } else if (component.types.indexOf("locality") >= 0) {
30411                 result.city = component.short_name;
30412             } else if (component.types.indexOf("sublocality") >= 0) {
30413                 result.district = component.short_name;
30414             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30415                 result.stateOrProvince = component.short_name;
30416             } else if (component.types.indexOf("country") >= 0) {
30417                 result.country = component.short_name;
30418             }
30419         }
30420         
30421         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30422         result.addressLine2 = "";
30423         return result;
30424     },
30425     
30426     setZoomLevel: function(zoom)
30427     {
30428         this.gMapContext.map.setZoom(zoom);
30429     },
30430     
30431     show: function()
30432     {
30433         if(!this.el){
30434             return;
30435         }
30436         
30437         this.el.show();
30438         
30439         this.resize();
30440         
30441         this.fireEvent('show', this);
30442     },
30443     
30444     hide: function()
30445     {
30446         if(!this.el){
30447             return;
30448         }
30449         
30450         this.el.hide();
30451         
30452         this.fireEvent('hide', this);
30453     }
30454     
30455 });
30456
30457 Roo.apply(Roo.bootstrap.LocationPicker, {
30458     
30459     OverlayView : function(map, options)
30460     {
30461         options = options || {};
30462         
30463         this.setMap(map);
30464     }
30465     
30466     
30467 });/**
30468  * @class Roo.bootstrap.Alert
30469  * @extends Roo.bootstrap.Component
30470  * Bootstrap Alert class - shows an alert area box
30471  * eg
30472  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30473   Enter a valid email address
30474 </div>
30475  * @licence LGPL
30476  * @cfg {String} title The title of alert
30477  * @cfg {String} html The content of alert
30478  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30479  * @cfg {String} fa font-awesomeicon
30480  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30481  * @cfg {Boolean} close true to show a x closer
30482  * 
30483  * 
30484  * @constructor
30485  * Create a new alert
30486  * @param {Object} config The config object
30487  */
30488
30489
30490 Roo.bootstrap.Alert = function(config){
30491     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30492     
30493 };
30494
30495 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30496     
30497     title: '',
30498     html: '',
30499     weight: false,
30500     fa: false,
30501     faicon: false, // BC
30502     close : false,
30503     
30504     
30505     getAutoCreate : function()
30506     {
30507         
30508         var cfg = {
30509             tag : 'div',
30510             cls : 'alert',
30511             cn : [
30512                 {
30513                     tag: 'button',
30514                     type :  "button",
30515                     cls: "close",
30516                     html : '×',
30517                     style : this.close ? '' : 'display:none'
30518                 },
30519                 {
30520                     tag : 'i',
30521                     cls : 'roo-alert-icon'
30522                     
30523                 },
30524                 {
30525                     tag : 'b',
30526                     cls : 'roo-alert-title',
30527                     html : this.title
30528                 },
30529                 {
30530                     tag : 'span',
30531                     cls : 'roo-alert-text',
30532                     html : this.html
30533                 }
30534             ]
30535         };
30536         
30537         if(this.faicon){
30538             cfg.cn[0].cls += ' fa ' + this.faicon;
30539         }
30540         if(this.fa){
30541             cfg.cn[0].cls += ' fa ' + this.fa;
30542         }
30543         
30544         if(this.weight){
30545             cfg.cls += ' alert-' + this.weight;
30546         }
30547         
30548         return cfg;
30549     },
30550     
30551     initEvents: function() 
30552     {
30553         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30554         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30555         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30556         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30557         if (this.seconds > 0) {
30558             this.hide.defer(this.seconds, this);
30559         }
30560     },
30561     /**
30562      * Set the Title Message HTML
30563      * @param {String} html
30564      */
30565     setTitle : function(str)
30566     {
30567         this.titleEl.dom.innerHTML = str;
30568     },
30569      
30570      /**
30571      * Set the Body Message HTML
30572      * @param {String} html
30573      */
30574     setHtml : function(str)
30575     {
30576         this.htmlEl.dom.innerHTML = str;
30577     },
30578     /**
30579      * Set the Weight of the alert
30580      * @param {String} (success|info|warning|danger) weight
30581      */
30582     
30583     setWeight : function(weight)
30584     {
30585         if(this.weight){
30586             this.el.removeClass('alert-' + this.weight);
30587         }
30588         
30589         this.weight = weight;
30590         
30591         this.el.addClass('alert-' + this.weight);
30592     },
30593       /**
30594      * Set the Icon of the alert
30595      * @param {String} see fontawsome names (name without the 'fa-' bit)
30596      */
30597     setIcon : function(icon)
30598     {
30599         if(this.faicon){
30600             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30601         }
30602         
30603         this.faicon = icon;
30604         
30605         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30606     },
30607     /**
30608      * Hide the Alert
30609      */
30610     hide: function() 
30611     {
30612         this.el.hide();   
30613     },
30614     /**
30615      * Show the Alert
30616      */
30617     show: function() 
30618     {  
30619         this.el.show();   
30620     }
30621     
30622 });
30623
30624  
30625 /*
30626 * Licence: LGPL
30627 */
30628
30629 /**
30630  * @class Roo.bootstrap.UploadCropbox
30631  * @extends Roo.bootstrap.Component
30632  * Bootstrap UploadCropbox class
30633  * @cfg {String} emptyText show when image has been loaded
30634  * @cfg {String} rotateNotify show when image too small to rotate
30635  * @cfg {Number} errorTimeout default 3000
30636  * @cfg {Number} minWidth default 300
30637  * @cfg {Number} minHeight default 300
30638  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30639  * @cfg {Boolean} isDocument (true|false) default false
30640  * @cfg {String} url action url
30641  * @cfg {String} paramName default 'imageUpload'
30642  * @cfg {String} method default POST
30643  * @cfg {Boolean} loadMask (true|false) default true
30644  * @cfg {Boolean} loadingText default 'Loading...'
30645  * 
30646  * @constructor
30647  * Create a new UploadCropbox
30648  * @param {Object} config The config object
30649  */
30650
30651 Roo.bootstrap.UploadCropbox = function(config){
30652     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30653     
30654     this.addEvents({
30655         /**
30656          * @event beforeselectfile
30657          * Fire before select file
30658          * @param {Roo.bootstrap.UploadCropbox} this
30659          */
30660         "beforeselectfile" : true,
30661         /**
30662          * @event initial
30663          * Fire after initEvent
30664          * @param {Roo.bootstrap.UploadCropbox} this
30665          */
30666         "initial" : true,
30667         /**
30668          * @event crop
30669          * Fire after initEvent
30670          * @param {Roo.bootstrap.UploadCropbox} this
30671          * @param {String} data
30672          */
30673         "crop" : true,
30674         /**
30675          * @event prepare
30676          * Fire when preparing the file data
30677          * @param {Roo.bootstrap.UploadCropbox} this
30678          * @param {Object} file
30679          */
30680         "prepare" : true,
30681         /**
30682          * @event exception
30683          * Fire when get exception
30684          * @param {Roo.bootstrap.UploadCropbox} this
30685          * @param {XMLHttpRequest} xhr
30686          */
30687         "exception" : true,
30688         /**
30689          * @event beforeloadcanvas
30690          * Fire before load the canvas
30691          * @param {Roo.bootstrap.UploadCropbox} this
30692          * @param {String} src
30693          */
30694         "beforeloadcanvas" : true,
30695         /**
30696          * @event trash
30697          * Fire when trash image
30698          * @param {Roo.bootstrap.UploadCropbox} this
30699          */
30700         "trash" : true,
30701         /**
30702          * @event download
30703          * Fire when download the image
30704          * @param {Roo.bootstrap.UploadCropbox} this
30705          */
30706         "download" : true,
30707         /**
30708          * @event footerbuttonclick
30709          * Fire when footerbuttonclick
30710          * @param {Roo.bootstrap.UploadCropbox} this
30711          * @param {String} type
30712          */
30713         "footerbuttonclick" : true,
30714         /**
30715          * @event resize
30716          * Fire when resize
30717          * @param {Roo.bootstrap.UploadCropbox} this
30718          */
30719         "resize" : true,
30720         /**
30721          * @event rotate
30722          * Fire when rotate the image
30723          * @param {Roo.bootstrap.UploadCropbox} this
30724          * @param {String} pos
30725          */
30726         "rotate" : true,
30727         /**
30728          * @event inspect
30729          * Fire when inspect the file
30730          * @param {Roo.bootstrap.UploadCropbox} this
30731          * @param {Object} file
30732          */
30733         "inspect" : true,
30734         /**
30735          * @event upload
30736          * Fire when xhr upload the file
30737          * @param {Roo.bootstrap.UploadCropbox} this
30738          * @param {Object} data
30739          */
30740         "upload" : true,
30741         /**
30742          * @event arrange
30743          * Fire when arrange the file data
30744          * @param {Roo.bootstrap.UploadCropbox} this
30745          * @param {Object} formData
30746          */
30747         "arrange" : true
30748     });
30749     
30750     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30751 };
30752
30753 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30754     
30755     emptyText : 'Click to upload image',
30756     rotateNotify : 'Image is too small to rotate',
30757     errorTimeout : 3000,
30758     scale : 0,
30759     baseScale : 1,
30760     rotate : 0,
30761     dragable : false,
30762     pinching : false,
30763     mouseX : 0,
30764     mouseY : 0,
30765     cropData : false,
30766     minWidth : 300,
30767     minHeight : 300,
30768     file : false,
30769     exif : {},
30770     baseRotate : 1,
30771     cropType : 'image/jpeg',
30772     buttons : false,
30773     canvasLoaded : false,
30774     isDocument : false,
30775     method : 'POST',
30776     paramName : 'imageUpload',
30777     loadMask : true,
30778     loadingText : 'Loading...',
30779     maskEl : false,
30780     
30781     getAutoCreate : function()
30782     {
30783         var cfg = {
30784             tag : 'div',
30785             cls : 'roo-upload-cropbox',
30786             cn : [
30787                 {
30788                     tag : 'input',
30789                     cls : 'roo-upload-cropbox-selector',
30790                     type : 'file'
30791                 },
30792                 {
30793                     tag : 'div',
30794                     cls : 'roo-upload-cropbox-body',
30795                     style : 'cursor:pointer',
30796                     cn : [
30797                         {
30798                             tag : 'div',
30799                             cls : 'roo-upload-cropbox-preview'
30800                         },
30801                         {
30802                             tag : 'div',
30803                             cls : 'roo-upload-cropbox-thumb'
30804                         },
30805                         {
30806                             tag : 'div',
30807                             cls : 'roo-upload-cropbox-empty-notify',
30808                             html : this.emptyText
30809                         },
30810                         {
30811                             tag : 'div',
30812                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30813                             html : this.rotateNotify
30814                         }
30815                     ]
30816                 },
30817                 {
30818                     tag : 'div',
30819                     cls : 'roo-upload-cropbox-footer',
30820                     cn : {
30821                         tag : 'div',
30822                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30823                         cn : []
30824                     }
30825                 }
30826             ]
30827         };
30828         
30829         return cfg;
30830     },
30831     
30832     onRender : function(ct, position)
30833     {
30834         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30835         
30836         if (this.buttons.length) {
30837             
30838             Roo.each(this.buttons, function(bb) {
30839                 
30840                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30841                 
30842                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30843                 
30844             }, this);
30845         }
30846         
30847         if(this.loadMask){
30848             this.maskEl = this.el;
30849         }
30850     },
30851     
30852     initEvents : function()
30853     {
30854         this.urlAPI = (window.createObjectURL && window) || 
30855                                 (window.URL && URL.revokeObjectURL && URL) || 
30856                                 (window.webkitURL && webkitURL);
30857                         
30858         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30859         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30860         
30861         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30862         this.selectorEl.hide();
30863         
30864         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30865         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30866         
30867         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30868         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30869         this.thumbEl.hide();
30870         
30871         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30872         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30873         
30874         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30875         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30876         this.errorEl.hide();
30877         
30878         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30879         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30880         this.footerEl.hide();
30881         
30882         this.setThumbBoxSize();
30883         
30884         this.bind();
30885         
30886         this.resize();
30887         
30888         this.fireEvent('initial', this);
30889     },
30890
30891     bind : function()
30892     {
30893         var _this = this;
30894         
30895         window.addEventListener("resize", function() { _this.resize(); } );
30896         
30897         this.bodyEl.on('click', this.beforeSelectFile, this);
30898         
30899         if(Roo.isTouch){
30900             this.bodyEl.on('touchstart', this.onTouchStart, this);
30901             this.bodyEl.on('touchmove', this.onTouchMove, this);
30902             this.bodyEl.on('touchend', this.onTouchEnd, this);
30903         }
30904         
30905         if(!Roo.isTouch){
30906             this.bodyEl.on('mousedown', this.onMouseDown, this);
30907             this.bodyEl.on('mousemove', this.onMouseMove, this);
30908             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30909             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30910             Roo.get(document).on('mouseup', this.onMouseUp, this);
30911         }
30912         
30913         this.selectorEl.on('change', this.onFileSelected, this);
30914     },
30915     
30916     reset : function()
30917     {    
30918         this.scale = 0;
30919         this.baseScale = 1;
30920         this.rotate = 0;
30921         this.baseRotate = 1;
30922         this.dragable = false;
30923         this.pinching = false;
30924         this.mouseX = 0;
30925         this.mouseY = 0;
30926         this.cropData = false;
30927         this.notifyEl.dom.innerHTML = this.emptyText;
30928         
30929         this.selectorEl.dom.value = '';
30930         
30931     },
30932     
30933     resize : function()
30934     {
30935         if(this.fireEvent('resize', this) != false){
30936             this.setThumbBoxPosition();
30937             this.setCanvasPosition();
30938         }
30939     },
30940     
30941     onFooterButtonClick : function(e, el, o, type)
30942     {
30943         switch (type) {
30944             case 'rotate-left' :
30945                 this.onRotateLeft(e);
30946                 break;
30947             case 'rotate-right' :
30948                 this.onRotateRight(e);
30949                 break;
30950             case 'picture' :
30951                 this.beforeSelectFile(e);
30952                 break;
30953             case 'trash' :
30954                 this.trash(e);
30955                 break;
30956             case 'crop' :
30957                 this.crop(e);
30958                 break;
30959             case 'download' :
30960                 this.download(e);
30961                 break;
30962             default :
30963                 break;
30964         }
30965         
30966         this.fireEvent('footerbuttonclick', this, type);
30967     },
30968     
30969     beforeSelectFile : function(e)
30970     {
30971         e.preventDefault();
30972         
30973         if(this.fireEvent('beforeselectfile', this) != false){
30974             this.selectorEl.dom.click();
30975         }
30976     },
30977     
30978     onFileSelected : function(e)
30979     {
30980         e.preventDefault();
30981         
30982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30983             return;
30984         }
30985         
30986         var file = this.selectorEl.dom.files[0];
30987         
30988         if(this.fireEvent('inspect', this, file) != false){
30989             this.prepare(file);
30990         }
30991         
30992     },
30993     
30994     trash : function(e)
30995     {
30996         this.fireEvent('trash', this);
30997     },
30998     
30999     download : function(e)
31000     {
31001         this.fireEvent('download', this);
31002     },
31003     
31004     loadCanvas : function(src)
31005     {   
31006         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31007             
31008             this.reset();
31009             
31010             this.imageEl = document.createElement('img');
31011             
31012             var _this = this;
31013             
31014             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31015             
31016             this.imageEl.src = src;
31017         }
31018     },
31019     
31020     onLoadCanvas : function()
31021     {   
31022         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31023         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31024         
31025         this.bodyEl.un('click', this.beforeSelectFile, this);
31026         
31027         this.notifyEl.hide();
31028         this.thumbEl.show();
31029         this.footerEl.show();
31030         
31031         this.baseRotateLevel();
31032         
31033         if(this.isDocument){
31034             this.setThumbBoxSize();
31035         }
31036         
31037         this.setThumbBoxPosition();
31038         
31039         this.baseScaleLevel();
31040         
31041         this.draw();
31042         
31043         this.resize();
31044         
31045         this.canvasLoaded = true;
31046         
31047         if(this.loadMask){
31048             this.maskEl.unmask();
31049         }
31050         
31051     },
31052     
31053     setCanvasPosition : function()
31054     {   
31055         if(!this.canvasEl){
31056             return;
31057         }
31058         
31059         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31060         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31061         
31062         this.previewEl.setLeft(pw);
31063         this.previewEl.setTop(ph);
31064         
31065     },
31066     
31067     onMouseDown : function(e)
31068     {   
31069         e.stopEvent();
31070         
31071         this.dragable = true;
31072         this.pinching = false;
31073         
31074         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31075             this.dragable = false;
31076             return;
31077         }
31078         
31079         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31080         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31081         
31082     },
31083     
31084     onMouseMove : function(e)
31085     {   
31086         e.stopEvent();
31087         
31088         if(!this.canvasLoaded){
31089             return;
31090         }
31091         
31092         if (!this.dragable){
31093             return;
31094         }
31095         
31096         var minX = Math.ceil(this.thumbEl.getLeft(true));
31097         var minY = Math.ceil(this.thumbEl.getTop(true));
31098         
31099         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31100         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31101         
31102         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31103         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31104         
31105         x = x - this.mouseX;
31106         y = y - this.mouseY;
31107         
31108         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31109         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31110         
31111         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31112         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31113         
31114         this.previewEl.setLeft(bgX);
31115         this.previewEl.setTop(bgY);
31116         
31117         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31118         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31119     },
31120     
31121     onMouseUp : function(e)
31122     {   
31123         e.stopEvent();
31124         
31125         this.dragable = false;
31126     },
31127     
31128     onMouseWheel : function(e)
31129     {   
31130         e.stopEvent();
31131         
31132         this.startScale = this.scale;
31133         
31134         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31135         
31136         if(!this.zoomable()){
31137             this.scale = this.startScale;
31138             return;
31139         }
31140         
31141         this.draw();
31142         
31143         return;
31144     },
31145     
31146     zoomable : function()
31147     {
31148         var minScale = this.thumbEl.getWidth() / this.minWidth;
31149         
31150         if(this.minWidth < this.minHeight){
31151             minScale = this.thumbEl.getHeight() / this.minHeight;
31152         }
31153         
31154         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31155         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31156         
31157         if(
31158                 this.isDocument &&
31159                 (this.rotate == 0 || this.rotate == 180) && 
31160                 (
31161                     width > this.imageEl.OriginWidth || 
31162                     height > this.imageEl.OriginHeight ||
31163                     (width < this.minWidth && height < this.minHeight)
31164                 )
31165         ){
31166             return false;
31167         }
31168         
31169         if(
31170                 this.isDocument &&
31171                 (this.rotate == 90 || this.rotate == 270) && 
31172                 (
31173                     width > this.imageEl.OriginWidth || 
31174                     height > this.imageEl.OriginHeight ||
31175                     (width < this.minHeight && height < this.minWidth)
31176                 )
31177         ){
31178             return false;
31179         }
31180         
31181         if(
31182                 !this.isDocument &&
31183                 (this.rotate == 0 || this.rotate == 180) && 
31184                 (
31185                     width < this.minWidth || 
31186                     width > this.imageEl.OriginWidth || 
31187                     height < this.minHeight || 
31188                     height > this.imageEl.OriginHeight
31189                 )
31190         ){
31191             return false;
31192         }
31193         
31194         if(
31195                 !this.isDocument &&
31196                 (this.rotate == 90 || this.rotate == 270) && 
31197                 (
31198                     width < this.minHeight || 
31199                     width > this.imageEl.OriginWidth || 
31200                     height < this.minWidth || 
31201                     height > this.imageEl.OriginHeight
31202                 )
31203         ){
31204             return false;
31205         }
31206         
31207         return true;
31208         
31209     },
31210     
31211     onRotateLeft : function(e)
31212     {   
31213         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31214             
31215             var minScale = this.thumbEl.getWidth() / this.minWidth;
31216             
31217             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31218             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31219             
31220             this.startScale = this.scale;
31221             
31222             while (this.getScaleLevel() < minScale){
31223             
31224                 this.scale = this.scale + 1;
31225                 
31226                 if(!this.zoomable()){
31227                     break;
31228                 }
31229                 
31230                 if(
31231                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31232                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31233                 ){
31234                     continue;
31235                 }
31236                 
31237                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31238
31239                 this.draw();
31240                 
31241                 return;
31242             }
31243             
31244             this.scale = this.startScale;
31245             
31246             this.onRotateFail();
31247             
31248             return false;
31249         }
31250         
31251         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31252
31253         if(this.isDocument){
31254             this.setThumbBoxSize();
31255             this.setThumbBoxPosition();
31256             this.setCanvasPosition();
31257         }
31258         
31259         this.draw();
31260         
31261         this.fireEvent('rotate', this, 'left');
31262         
31263     },
31264     
31265     onRotateRight : function(e)
31266     {
31267         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31268             
31269             var minScale = this.thumbEl.getWidth() / this.minWidth;
31270         
31271             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31272             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31273             
31274             this.startScale = this.scale;
31275             
31276             while (this.getScaleLevel() < minScale){
31277             
31278                 this.scale = this.scale + 1;
31279                 
31280                 if(!this.zoomable()){
31281                     break;
31282                 }
31283                 
31284                 if(
31285                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31286                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31287                 ){
31288                     continue;
31289                 }
31290                 
31291                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31292
31293                 this.draw();
31294                 
31295                 return;
31296             }
31297             
31298             this.scale = this.startScale;
31299             
31300             this.onRotateFail();
31301             
31302             return false;
31303         }
31304         
31305         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31306
31307         if(this.isDocument){
31308             this.setThumbBoxSize();
31309             this.setThumbBoxPosition();
31310             this.setCanvasPosition();
31311         }
31312         
31313         this.draw();
31314         
31315         this.fireEvent('rotate', this, 'right');
31316     },
31317     
31318     onRotateFail : function()
31319     {
31320         this.errorEl.show(true);
31321         
31322         var _this = this;
31323         
31324         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31325     },
31326     
31327     draw : function()
31328     {
31329         this.previewEl.dom.innerHTML = '';
31330         
31331         var canvasEl = document.createElement("canvas");
31332         
31333         var contextEl = canvasEl.getContext("2d");
31334         
31335         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31336         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31337         var center = this.imageEl.OriginWidth / 2;
31338         
31339         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31340             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31341             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31342             center = this.imageEl.OriginHeight / 2;
31343         }
31344         
31345         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31346         
31347         contextEl.translate(center, center);
31348         contextEl.rotate(this.rotate * Math.PI / 180);
31349
31350         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31351         
31352         this.canvasEl = document.createElement("canvas");
31353         
31354         this.contextEl = this.canvasEl.getContext("2d");
31355         
31356         switch (this.rotate) {
31357             case 0 :
31358                 
31359                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31360                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31361                 
31362                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31363                 
31364                 break;
31365             case 90 : 
31366                 
31367                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31368                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31369                 
31370                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31371                     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);
31372                     break;
31373                 }
31374                 
31375                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31376                 
31377                 break;
31378             case 180 :
31379                 
31380                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31381                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31382                 
31383                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31384                     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);
31385                     break;
31386                 }
31387                 
31388                 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);
31389                 
31390                 break;
31391             case 270 :
31392                 
31393                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31394                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31395         
31396                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31397                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31398                     break;
31399                 }
31400                 
31401                 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);
31402                 
31403                 break;
31404             default : 
31405                 break;
31406         }
31407         
31408         this.previewEl.appendChild(this.canvasEl);
31409         
31410         this.setCanvasPosition();
31411     },
31412     
31413     crop : function()
31414     {
31415         if(!this.canvasLoaded){
31416             return;
31417         }
31418         
31419         var imageCanvas = document.createElement("canvas");
31420         
31421         var imageContext = imageCanvas.getContext("2d");
31422         
31423         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31424         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31425         
31426         var center = imageCanvas.width / 2;
31427         
31428         imageContext.translate(center, center);
31429         
31430         imageContext.rotate(this.rotate * Math.PI / 180);
31431         
31432         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31433         
31434         var canvas = document.createElement("canvas");
31435         
31436         var context = canvas.getContext("2d");
31437                 
31438         canvas.width = this.minWidth;
31439         canvas.height = this.minHeight;
31440
31441         switch (this.rotate) {
31442             case 0 :
31443                 
31444                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31445                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31446                 
31447                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31448                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31449                 
31450                 var targetWidth = this.minWidth - 2 * x;
31451                 var targetHeight = this.minHeight - 2 * y;
31452                 
31453                 var scale = 1;
31454                 
31455                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31456                     scale = targetWidth / width;
31457                 }
31458                 
31459                 if(x > 0 && y == 0){
31460                     scale = targetHeight / height;
31461                 }
31462                 
31463                 if(x > 0 && y > 0){
31464                     scale = targetWidth / width;
31465                     
31466                     if(width < height){
31467                         scale = targetHeight / height;
31468                     }
31469                 }
31470                 
31471                 context.scale(scale, scale);
31472                 
31473                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31474                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31475
31476                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31477                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31478
31479                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31480                 
31481                 break;
31482             case 90 : 
31483                 
31484                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31485                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31486                 
31487                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31488                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31489                 
31490                 var targetWidth = this.minWidth - 2 * x;
31491                 var targetHeight = this.minHeight - 2 * y;
31492                 
31493                 var scale = 1;
31494                 
31495                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31496                     scale = targetWidth / width;
31497                 }
31498                 
31499                 if(x > 0 && y == 0){
31500                     scale = targetHeight / height;
31501                 }
31502                 
31503                 if(x > 0 && y > 0){
31504                     scale = targetWidth / width;
31505                     
31506                     if(width < height){
31507                         scale = targetHeight / height;
31508                     }
31509                 }
31510                 
31511                 context.scale(scale, scale);
31512                 
31513                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31514                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31515
31516                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31517                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31518                 
31519                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31520                 
31521                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31522                 
31523                 break;
31524             case 180 :
31525                 
31526                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31527                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31528                 
31529                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31530                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31531                 
31532                 var targetWidth = this.minWidth - 2 * x;
31533                 var targetHeight = this.minHeight - 2 * y;
31534                 
31535                 var scale = 1;
31536                 
31537                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31538                     scale = targetWidth / width;
31539                 }
31540                 
31541                 if(x > 0 && y == 0){
31542                     scale = targetHeight / height;
31543                 }
31544                 
31545                 if(x > 0 && y > 0){
31546                     scale = targetWidth / width;
31547                     
31548                     if(width < height){
31549                         scale = targetHeight / height;
31550                     }
31551                 }
31552                 
31553                 context.scale(scale, scale);
31554                 
31555                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31556                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31557
31558                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31559                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31560
31561                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31562                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31563                 
31564                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31565                 
31566                 break;
31567             case 270 :
31568                 
31569                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31570                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31571                 
31572                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31573                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31574                 
31575                 var targetWidth = this.minWidth - 2 * x;
31576                 var targetHeight = this.minHeight - 2 * y;
31577                 
31578                 var scale = 1;
31579                 
31580                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31581                     scale = targetWidth / width;
31582                 }
31583                 
31584                 if(x > 0 && y == 0){
31585                     scale = targetHeight / height;
31586                 }
31587                 
31588                 if(x > 0 && y > 0){
31589                     scale = targetWidth / width;
31590                     
31591                     if(width < height){
31592                         scale = targetHeight / height;
31593                     }
31594                 }
31595                 
31596                 context.scale(scale, scale);
31597                 
31598                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31599                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31600
31601                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31602                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31603                 
31604                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31605                 
31606                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31607                 
31608                 break;
31609             default : 
31610                 break;
31611         }
31612         
31613         this.cropData = canvas.toDataURL(this.cropType);
31614         
31615         if(this.fireEvent('crop', this, this.cropData) !== false){
31616             this.process(this.file, this.cropData);
31617         }
31618         
31619         return;
31620         
31621     },
31622     
31623     setThumbBoxSize : function()
31624     {
31625         var width, height;
31626         
31627         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31628             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31629             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31630             
31631             this.minWidth = width;
31632             this.minHeight = height;
31633             
31634             if(this.rotate == 90 || this.rotate == 270){
31635                 this.minWidth = height;
31636                 this.minHeight = width;
31637             }
31638         }
31639         
31640         height = 300;
31641         width = Math.ceil(this.minWidth * height / this.minHeight);
31642         
31643         if(this.minWidth > this.minHeight){
31644             width = 300;
31645             height = Math.ceil(this.minHeight * width / this.minWidth);
31646         }
31647         
31648         this.thumbEl.setStyle({
31649             width : width + 'px',
31650             height : height + 'px'
31651         });
31652
31653         return;
31654             
31655     },
31656     
31657     setThumbBoxPosition : function()
31658     {
31659         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31660         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31661         
31662         this.thumbEl.setLeft(x);
31663         this.thumbEl.setTop(y);
31664         
31665     },
31666     
31667     baseRotateLevel : function()
31668     {
31669         this.baseRotate = 1;
31670         
31671         if(
31672                 typeof(this.exif) != 'undefined' &&
31673                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31674                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31675         ){
31676             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31677         }
31678         
31679         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31680         
31681     },
31682     
31683     baseScaleLevel : function()
31684     {
31685         var width, height;
31686         
31687         if(this.isDocument){
31688             
31689             if(this.baseRotate == 6 || this.baseRotate == 8){
31690             
31691                 height = this.thumbEl.getHeight();
31692                 this.baseScale = height / this.imageEl.OriginWidth;
31693
31694                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31695                     width = this.thumbEl.getWidth();
31696                     this.baseScale = width / this.imageEl.OriginHeight;
31697                 }
31698
31699                 return;
31700             }
31701
31702             height = this.thumbEl.getHeight();
31703             this.baseScale = height / this.imageEl.OriginHeight;
31704
31705             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31706                 width = this.thumbEl.getWidth();
31707                 this.baseScale = width / this.imageEl.OriginWidth;
31708             }
31709
31710             return;
31711         }
31712         
31713         if(this.baseRotate == 6 || this.baseRotate == 8){
31714             
31715             width = this.thumbEl.getHeight();
31716             this.baseScale = width / this.imageEl.OriginHeight;
31717             
31718             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31719                 height = this.thumbEl.getWidth();
31720                 this.baseScale = height / this.imageEl.OriginHeight;
31721             }
31722             
31723             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31724                 height = this.thumbEl.getWidth();
31725                 this.baseScale = height / this.imageEl.OriginHeight;
31726                 
31727                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31728                     width = this.thumbEl.getHeight();
31729                     this.baseScale = width / this.imageEl.OriginWidth;
31730                 }
31731             }
31732             
31733             return;
31734         }
31735         
31736         width = this.thumbEl.getWidth();
31737         this.baseScale = width / this.imageEl.OriginWidth;
31738         
31739         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31740             height = this.thumbEl.getHeight();
31741             this.baseScale = height / this.imageEl.OriginHeight;
31742         }
31743         
31744         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31745             
31746             height = this.thumbEl.getHeight();
31747             this.baseScale = height / this.imageEl.OriginHeight;
31748             
31749             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31750                 width = this.thumbEl.getWidth();
31751                 this.baseScale = width / this.imageEl.OriginWidth;
31752             }
31753             
31754         }
31755         
31756         return;
31757     },
31758     
31759     getScaleLevel : function()
31760     {
31761         return this.baseScale * Math.pow(1.1, this.scale);
31762     },
31763     
31764     onTouchStart : function(e)
31765     {
31766         if(!this.canvasLoaded){
31767             this.beforeSelectFile(e);
31768             return;
31769         }
31770         
31771         var touches = e.browserEvent.touches;
31772         
31773         if(!touches){
31774             return;
31775         }
31776         
31777         if(touches.length == 1){
31778             this.onMouseDown(e);
31779             return;
31780         }
31781         
31782         if(touches.length != 2){
31783             return;
31784         }
31785         
31786         var coords = [];
31787         
31788         for(var i = 0, finger; finger = touches[i]; i++){
31789             coords.push(finger.pageX, finger.pageY);
31790         }
31791         
31792         var x = Math.pow(coords[0] - coords[2], 2);
31793         var y = Math.pow(coords[1] - coords[3], 2);
31794         
31795         this.startDistance = Math.sqrt(x + y);
31796         
31797         this.startScale = this.scale;
31798         
31799         this.pinching = true;
31800         this.dragable = false;
31801         
31802     },
31803     
31804     onTouchMove : function(e)
31805     {
31806         if(!this.pinching && !this.dragable){
31807             return;
31808         }
31809         
31810         var touches = e.browserEvent.touches;
31811         
31812         if(!touches){
31813             return;
31814         }
31815         
31816         if(this.dragable){
31817             this.onMouseMove(e);
31818             return;
31819         }
31820         
31821         var coords = [];
31822         
31823         for(var i = 0, finger; finger = touches[i]; i++){
31824             coords.push(finger.pageX, finger.pageY);
31825         }
31826         
31827         var x = Math.pow(coords[0] - coords[2], 2);
31828         var y = Math.pow(coords[1] - coords[3], 2);
31829         
31830         this.endDistance = Math.sqrt(x + y);
31831         
31832         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31833         
31834         if(!this.zoomable()){
31835             this.scale = this.startScale;
31836             return;
31837         }
31838         
31839         this.draw();
31840         
31841     },
31842     
31843     onTouchEnd : function(e)
31844     {
31845         this.pinching = false;
31846         this.dragable = false;
31847         
31848     },
31849     
31850     process : function(file, crop)
31851     {
31852         if(this.loadMask){
31853             this.maskEl.mask(this.loadingText);
31854         }
31855         
31856         this.xhr = new XMLHttpRequest();
31857         
31858         file.xhr = this.xhr;
31859
31860         this.xhr.open(this.method, this.url, true);
31861         
31862         var headers = {
31863             "Accept": "application/json",
31864             "Cache-Control": "no-cache",
31865             "X-Requested-With": "XMLHttpRequest"
31866         };
31867         
31868         for (var headerName in headers) {
31869             var headerValue = headers[headerName];
31870             if (headerValue) {
31871                 this.xhr.setRequestHeader(headerName, headerValue);
31872             }
31873         }
31874         
31875         var _this = this;
31876         
31877         this.xhr.onload = function()
31878         {
31879             _this.xhrOnLoad(_this.xhr);
31880         }
31881         
31882         this.xhr.onerror = function()
31883         {
31884             _this.xhrOnError(_this.xhr);
31885         }
31886         
31887         var formData = new FormData();
31888
31889         formData.append('returnHTML', 'NO');
31890         
31891         if(crop){
31892             formData.append('crop', crop);
31893         }
31894         
31895         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31896             formData.append(this.paramName, file, file.name);
31897         }
31898         
31899         if(typeof(file.filename) != 'undefined'){
31900             formData.append('filename', file.filename);
31901         }
31902         
31903         if(typeof(file.mimetype) != 'undefined'){
31904             formData.append('mimetype', file.mimetype);
31905         }
31906         
31907         if(this.fireEvent('arrange', this, formData) != false){
31908             this.xhr.send(formData);
31909         };
31910     },
31911     
31912     xhrOnLoad : function(xhr)
31913     {
31914         if(this.loadMask){
31915             this.maskEl.unmask();
31916         }
31917         
31918         if (xhr.readyState !== 4) {
31919             this.fireEvent('exception', this, xhr);
31920             return;
31921         }
31922
31923         var response = Roo.decode(xhr.responseText);
31924         
31925         if(!response.success){
31926             this.fireEvent('exception', this, xhr);
31927             return;
31928         }
31929         
31930         var response = Roo.decode(xhr.responseText);
31931         
31932         this.fireEvent('upload', this, response);
31933         
31934     },
31935     
31936     xhrOnError : function()
31937     {
31938         if(this.loadMask){
31939             this.maskEl.unmask();
31940         }
31941         
31942         Roo.log('xhr on error');
31943         
31944         var response = Roo.decode(xhr.responseText);
31945           
31946         Roo.log(response);
31947         
31948     },
31949     
31950     prepare : function(file)
31951     {   
31952         if(this.loadMask){
31953             this.maskEl.mask(this.loadingText);
31954         }
31955         
31956         this.file = false;
31957         this.exif = {};
31958         
31959         if(typeof(file) === 'string'){
31960             this.loadCanvas(file);
31961             return;
31962         }
31963         
31964         if(!file || !this.urlAPI){
31965             return;
31966         }
31967         
31968         this.file = file;
31969         this.cropType = file.type;
31970         
31971         var _this = this;
31972         
31973         if(this.fireEvent('prepare', this, this.file) != false){
31974             
31975             var reader = new FileReader();
31976             
31977             reader.onload = function (e) {
31978                 if (e.target.error) {
31979                     Roo.log(e.target.error);
31980                     return;
31981                 }
31982                 
31983                 var buffer = e.target.result,
31984                     dataView = new DataView(buffer),
31985                     offset = 2,
31986                     maxOffset = dataView.byteLength - 4,
31987                     markerBytes,
31988                     markerLength;
31989                 
31990                 if (dataView.getUint16(0) === 0xffd8) {
31991                     while (offset < maxOffset) {
31992                         markerBytes = dataView.getUint16(offset);
31993                         
31994                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31995                             markerLength = dataView.getUint16(offset + 2) + 2;
31996                             if (offset + markerLength > dataView.byteLength) {
31997                                 Roo.log('Invalid meta data: Invalid segment size.');
31998                                 break;
31999                             }
32000                             
32001                             if(markerBytes == 0xffe1){
32002                                 _this.parseExifData(
32003                                     dataView,
32004                                     offset,
32005                                     markerLength
32006                                 );
32007                             }
32008                             
32009                             offset += markerLength;
32010                             
32011                             continue;
32012                         }
32013                         
32014                         break;
32015                     }
32016                     
32017                 }
32018                 
32019                 var url = _this.urlAPI.createObjectURL(_this.file);
32020                 
32021                 _this.loadCanvas(url);
32022                 
32023                 return;
32024             }
32025             
32026             reader.readAsArrayBuffer(this.file);
32027             
32028         }
32029         
32030     },
32031     
32032     parseExifData : function(dataView, offset, length)
32033     {
32034         var tiffOffset = offset + 10,
32035             littleEndian,
32036             dirOffset;
32037     
32038         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32039             // No Exif data, might be XMP data instead
32040             return;
32041         }
32042         
32043         // Check for the ASCII code for "Exif" (0x45786966):
32044         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32045             // No Exif data, might be XMP data instead
32046             return;
32047         }
32048         if (tiffOffset + 8 > dataView.byteLength) {
32049             Roo.log('Invalid Exif data: Invalid segment size.');
32050             return;
32051         }
32052         // Check for the two null bytes:
32053         if (dataView.getUint16(offset + 8) !== 0x0000) {
32054             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32055             return;
32056         }
32057         // Check the byte alignment:
32058         switch (dataView.getUint16(tiffOffset)) {
32059         case 0x4949:
32060             littleEndian = true;
32061             break;
32062         case 0x4D4D:
32063             littleEndian = false;
32064             break;
32065         default:
32066             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32067             return;
32068         }
32069         // Check for the TIFF tag marker (0x002A):
32070         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32071             Roo.log('Invalid Exif data: Missing TIFF marker.');
32072             return;
32073         }
32074         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32075         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32076         
32077         this.parseExifTags(
32078             dataView,
32079             tiffOffset,
32080             tiffOffset + dirOffset,
32081             littleEndian
32082         );
32083     },
32084     
32085     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32086     {
32087         var tagsNumber,
32088             dirEndOffset,
32089             i;
32090         if (dirOffset + 6 > dataView.byteLength) {
32091             Roo.log('Invalid Exif data: Invalid directory offset.');
32092             return;
32093         }
32094         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32095         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32096         if (dirEndOffset + 4 > dataView.byteLength) {
32097             Roo.log('Invalid Exif data: Invalid directory size.');
32098             return;
32099         }
32100         for (i = 0; i < tagsNumber; i += 1) {
32101             this.parseExifTag(
32102                 dataView,
32103                 tiffOffset,
32104                 dirOffset + 2 + 12 * i, // tag offset
32105                 littleEndian
32106             );
32107         }
32108         // Return the offset to the next directory:
32109         return dataView.getUint32(dirEndOffset, littleEndian);
32110     },
32111     
32112     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32113     {
32114         var tag = dataView.getUint16(offset, littleEndian);
32115         
32116         this.exif[tag] = this.getExifValue(
32117             dataView,
32118             tiffOffset,
32119             offset,
32120             dataView.getUint16(offset + 2, littleEndian), // tag type
32121             dataView.getUint32(offset + 4, littleEndian), // tag length
32122             littleEndian
32123         );
32124     },
32125     
32126     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32127     {
32128         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32129             tagSize,
32130             dataOffset,
32131             values,
32132             i,
32133             str,
32134             c;
32135     
32136         if (!tagType) {
32137             Roo.log('Invalid Exif data: Invalid tag type.');
32138             return;
32139         }
32140         
32141         tagSize = tagType.size * length;
32142         // Determine if the value is contained in the dataOffset bytes,
32143         // or if the value at the dataOffset is a pointer to the actual data:
32144         dataOffset = tagSize > 4 ?
32145                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32146         if (dataOffset + tagSize > dataView.byteLength) {
32147             Roo.log('Invalid Exif data: Invalid data offset.');
32148             return;
32149         }
32150         if (length === 1) {
32151             return tagType.getValue(dataView, dataOffset, littleEndian);
32152         }
32153         values = [];
32154         for (i = 0; i < length; i += 1) {
32155             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32156         }
32157         
32158         if (tagType.ascii) {
32159             str = '';
32160             // Concatenate the chars:
32161             for (i = 0; i < values.length; i += 1) {
32162                 c = values[i];
32163                 // Ignore the terminating NULL byte(s):
32164                 if (c === '\u0000') {
32165                     break;
32166                 }
32167                 str += c;
32168             }
32169             return str;
32170         }
32171         return values;
32172     }
32173     
32174 });
32175
32176 Roo.apply(Roo.bootstrap.UploadCropbox, {
32177     tags : {
32178         'Orientation': 0x0112
32179     },
32180     
32181     Orientation: {
32182             1: 0, //'top-left',
32183 //            2: 'top-right',
32184             3: 180, //'bottom-right',
32185 //            4: 'bottom-left',
32186 //            5: 'left-top',
32187             6: 90, //'right-top',
32188 //            7: 'right-bottom',
32189             8: 270 //'left-bottom'
32190     },
32191     
32192     exifTagTypes : {
32193         // byte, 8-bit unsigned int:
32194         1: {
32195             getValue: function (dataView, dataOffset) {
32196                 return dataView.getUint8(dataOffset);
32197             },
32198             size: 1
32199         },
32200         // ascii, 8-bit byte:
32201         2: {
32202             getValue: function (dataView, dataOffset) {
32203                 return String.fromCharCode(dataView.getUint8(dataOffset));
32204             },
32205             size: 1,
32206             ascii: true
32207         },
32208         // short, 16 bit int:
32209         3: {
32210             getValue: function (dataView, dataOffset, littleEndian) {
32211                 return dataView.getUint16(dataOffset, littleEndian);
32212             },
32213             size: 2
32214         },
32215         // long, 32 bit int:
32216         4: {
32217             getValue: function (dataView, dataOffset, littleEndian) {
32218                 return dataView.getUint32(dataOffset, littleEndian);
32219             },
32220             size: 4
32221         },
32222         // rational = two long values, first is numerator, second is denominator:
32223         5: {
32224             getValue: function (dataView, dataOffset, littleEndian) {
32225                 return dataView.getUint32(dataOffset, littleEndian) /
32226                     dataView.getUint32(dataOffset + 4, littleEndian);
32227             },
32228             size: 8
32229         },
32230         // slong, 32 bit signed int:
32231         9: {
32232             getValue: function (dataView, dataOffset, littleEndian) {
32233                 return dataView.getInt32(dataOffset, littleEndian);
32234             },
32235             size: 4
32236         },
32237         // srational, two slongs, first is numerator, second is denominator:
32238         10: {
32239             getValue: function (dataView, dataOffset, littleEndian) {
32240                 return dataView.getInt32(dataOffset, littleEndian) /
32241                     dataView.getInt32(dataOffset + 4, littleEndian);
32242             },
32243             size: 8
32244         }
32245     },
32246     
32247     footer : {
32248         STANDARD : [
32249             {
32250                 tag : 'div',
32251                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32252                 action : 'rotate-left',
32253                 cn : [
32254                     {
32255                         tag : 'button',
32256                         cls : 'btn btn-default',
32257                         html : '<i class="fa fa-undo"></i>'
32258                     }
32259                 ]
32260             },
32261             {
32262                 tag : 'div',
32263                 cls : 'btn-group roo-upload-cropbox-picture',
32264                 action : 'picture',
32265                 cn : [
32266                     {
32267                         tag : 'button',
32268                         cls : 'btn btn-default',
32269                         html : '<i class="fa fa-picture-o"></i>'
32270                     }
32271                 ]
32272             },
32273             {
32274                 tag : 'div',
32275                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32276                 action : 'rotate-right',
32277                 cn : [
32278                     {
32279                         tag : 'button',
32280                         cls : 'btn btn-default',
32281                         html : '<i class="fa fa-repeat"></i>'
32282                     }
32283                 ]
32284             }
32285         ],
32286         DOCUMENT : [
32287             {
32288                 tag : 'div',
32289                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32290                 action : 'rotate-left',
32291                 cn : [
32292                     {
32293                         tag : 'button',
32294                         cls : 'btn btn-default',
32295                         html : '<i class="fa fa-undo"></i>'
32296                     }
32297                 ]
32298             },
32299             {
32300                 tag : 'div',
32301                 cls : 'btn-group roo-upload-cropbox-download',
32302                 action : 'download',
32303                 cn : [
32304                     {
32305                         tag : 'button',
32306                         cls : 'btn btn-default',
32307                         html : '<i class="fa fa-download"></i>'
32308                     }
32309                 ]
32310             },
32311             {
32312                 tag : 'div',
32313                 cls : 'btn-group roo-upload-cropbox-crop',
32314                 action : 'crop',
32315                 cn : [
32316                     {
32317                         tag : 'button',
32318                         cls : 'btn btn-default',
32319                         html : '<i class="fa fa-crop"></i>'
32320                     }
32321                 ]
32322             },
32323             {
32324                 tag : 'div',
32325                 cls : 'btn-group roo-upload-cropbox-trash',
32326                 action : 'trash',
32327                 cn : [
32328                     {
32329                         tag : 'button',
32330                         cls : 'btn btn-default',
32331                         html : '<i class="fa fa-trash"></i>'
32332                     }
32333                 ]
32334             },
32335             {
32336                 tag : 'div',
32337                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32338                 action : 'rotate-right',
32339                 cn : [
32340                     {
32341                         tag : 'button',
32342                         cls : 'btn btn-default',
32343                         html : '<i class="fa fa-repeat"></i>'
32344                     }
32345                 ]
32346             }
32347         ],
32348         ROTATOR : [
32349             {
32350                 tag : 'div',
32351                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32352                 action : 'rotate-left',
32353                 cn : [
32354                     {
32355                         tag : 'button',
32356                         cls : 'btn btn-default',
32357                         html : '<i class="fa fa-undo"></i>'
32358                     }
32359                 ]
32360             },
32361             {
32362                 tag : 'div',
32363                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32364                 action : 'rotate-right',
32365                 cn : [
32366                     {
32367                         tag : 'button',
32368                         cls : 'btn btn-default',
32369                         html : '<i class="fa fa-repeat"></i>'
32370                     }
32371                 ]
32372             }
32373         ]
32374     }
32375 });
32376
32377 /*
32378 * Licence: LGPL
32379 */
32380
32381 /**
32382  * @class Roo.bootstrap.DocumentManager
32383  * @extends Roo.bootstrap.Component
32384  * Bootstrap DocumentManager class
32385  * @cfg {String} paramName default 'imageUpload'
32386  * @cfg {String} toolTipName default 'filename'
32387  * @cfg {String} method default POST
32388  * @cfg {String} url action url
32389  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32390  * @cfg {Boolean} multiple multiple upload default true
32391  * @cfg {Number} thumbSize default 300
32392  * @cfg {String} fieldLabel
32393  * @cfg {Number} labelWidth default 4
32394  * @cfg {String} labelAlign (left|top) default left
32395  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32396 * @cfg {Number} labellg set the width of label (1-12)
32397  * @cfg {Number} labelmd set the width of label (1-12)
32398  * @cfg {Number} labelsm set the width of label (1-12)
32399  * @cfg {Number} labelxs set the width of label (1-12)
32400  * 
32401  * @constructor
32402  * Create a new DocumentManager
32403  * @param {Object} config The config object
32404  */
32405
32406 Roo.bootstrap.DocumentManager = function(config){
32407     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32408     
32409     this.files = [];
32410     this.delegates = [];
32411     
32412     this.addEvents({
32413         /**
32414          * @event initial
32415          * Fire when initial the DocumentManager
32416          * @param {Roo.bootstrap.DocumentManager} this
32417          */
32418         "initial" : true,
32419         /**
32420          * @event inspect
32421          * inspect selected file
32422          * @param {Roo.bootstrap.DocumentManager} this
32423          * @param {File} file
32424          */
32425         "inspect" : true,
32426         /**
32427          * @event exception
32428          * Fire when xhr load exception
32429          * @param {Roo.bootstrap.DocumentManager} this
32430          * @param {XMLHttpRequest} xhr
32431          */
32432         "exception" : true,
32433         /**
32434          * @event afterupload
32435          * Fire when xhr load exception
32436          * @param {Roo.bootstrap.DocumentManager} this
32437          * @param {XMLHttpRequest} xhr
32438          */
32439         "afterupload" : true,
32440         /**
32441          * @event prepare
32442          * prepare the form data
32443          * @param {Roo.bootstrap.DocumentManager} this
32444          * @param {Object} formData
32445          */
32446         "prepare" : true,
32447         /**
32448          * @event remove
32449          * Fire when remove the file
32450          * @param {Roo.bootstrap.DocumentManager} this
32451          * @param {Object} file
32452          */
32453         "remove" : true,
32454         /**
32455          * @event refresh
32456          * Fire after refresh the file
32457          * @param {Roo.bootstrap.DocumentManager} this
32458          */
32459         "refresh" : true,
32460         /**
32461          * @event click
32462          * Fire after click the image
32463          * @param {Roo.bootstrap.DocumentManager} this
32464          * @param {Object} file
32465          */
32466         "click" : true,
32467         /**
32468          * @event edit
32469          * Fire when upload a image and editable set to true
32470          * @param {Roo.bootstrap.DocumentManager} this
32471          * @param {Object} file
32472          */
32473         "edit" : true,
32474         /**
32475          * @event beforeselectfile
32476          * Fire before select file
32477          * @param {Roo.bootstrap.DocumentManager} this
32478          */
32479         "beforeselectfile" : true,
32480         /**
32481          * @event process
32482          * Fire before process file
32483          * @param {Roo.bootstrap.DocumentManager} this
32484          * @param {Object} file
32485          */
32486         "process" : true,
32487         /**
32488          * @event previewrendered
32489          * Fire when preview rendered
32490          * @param {Roo.bootstrap.DocumentManager} this
32491          * @param {Object} file
32492          */
32493         "previewrendered" : true,
32494         /**
32495          */
32496         "previewResize" : true
32497         
32498     });
32499 };
32500
32501 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32502     
32503     boxes : 0,
32504     inputName : '',
32505     thumbSize : 300,
32506     multiple : true,
32507     files : false,
32508     method : 'POST',
32509     url : '',
32510     paramName : 'imageUpload',
32511     toolTipName : 'filename',
32512     fieldLabel : '',
32513     labelWidth : 4,
32514     labelAlign : 'left',
32515     editable : true,
32516     delegates : false,
32517     xhr : false, 
32518     
32519     labellg : 0,
32520     labelmd : 0,
32521     labelsm : 0,
32522     labelxs : 0,
32523     
32524     getAutoCreate : function()
32525     {   
32526         var managerWidget = {
32527             tag : 'div',
32528             cls : 'roo-document-manager',
32529             cn : [
32530                 {
32531                     tag : 'input',
32532                     cls : 'roo-document-manager-selector',
32533                     type : 'file'
32534                 },
32535                 {
32536                     tag : 'div',
32537                     cls : 'roo-document-manager-uploader',
32538                     cn : [
32539                         {
32540                             tag : 'div',
32541                             cls : 'roo-document-manager-upload-btn',
32542                             html : '<i class="fa fa-plus"></i>'
32543                         }
32544                     ]
32545                     
32546                 }
32547             ]
32548         };
32549         
32550         var content = [
32551             {
32552                 tag : 'div',
32553                 cls : 'column col-md-12',
32554                 cn : managerWidget
32555             }
32556         ];
32557         
32558         if(this.fieldLabel.length){
32559             
32560             content = [
32561                 {
32562                     tag : 'div',
32563                     cls : 'column col-md-12',
32564                     html : this.fieldLabel
32565                 },
32566                 {
32567                     tag : 'div',
32568                     cls : 'column col-md-12',
32569                     cn : managerWidget
32570                 }
32571             ];
32572
32573             if(this.labelAlign == 'left'){
32574                 content = [
32575                     {
32576                         tag : 'div',
32577                         cls : 'column',
32578                         html : this.fieldLabel
32579                     },
32580                     {
32581                         tag : 'div',
32582                         cls : 'column',
32583                         cn : managerWidget
32584                     }
32585                 ];
32586                 
32587                 if(this.labelWidth > 12){
32588                     content[0].style = "width: " + this.labelWidth + 'px';
32589                 }
32590
32591                 if(this.labelWidth < 13 && this.labelmd == 0){
32592                     this.labelmd = this.labelWidth;
32593                 }
32594
32595                 if(this.labellg > 0){
32596                     content[0].cls += ' col-lg-' + this.labellg;
32597                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32598                 }
32599
32600                 if(this.labelmd > 0){
32601                     content[0].cls += ' col-md-' + this.labelmd;
32602                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32603                 }
32604
32605                 if(this.labelsm > 0){
32606                     content[0].cls += ' col-sm-' + this.labelsm;
32607                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32608                 }
32609
32610                 if(this.labelxs > 0){
32611                     content[0].cls += ' col-xs-' + this.labelxs;
32612                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32613                 }
32614                 
32615             }
32616         }
32617         
32618         var cfg = {
32619             tag : 'div',
32620             cls : 'row clearfix',
32621             cn : content
32622         };
32623         
32624         return cfg;
32625         
32626     },
32627     
32628     initEvents : function()
32629     {
32630         this.managerEl = this.el.select('.roo-document-manager', true).first();
32631         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32632         
32633         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32634         this.selectorEl.hide();
32635         
32636         if(this.multiple){
32637             this.selectorEl.attr('multiple', 'multiple');
32638         }
32639         
32640         this.selectorEl.on('change', this.onFileSelected, this);
32641         
32642         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32643         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32644         
32645         this.uploader.on('click', this.onUploaderClick, this);
32646         
32647         this.renderProgressDialog();
32648         
32649         var _this = this;
32650         
32651         window.addEventListener("resize", function() { _this.refresh(); } );
32652         
32653         this.fireEvent('initial', this);
32654     },
32655     
32656     renderProgressDialog : function()
32657     {
32658         var _this = this;
32659         
32660         this.progressDialog = new Roo.bootstrap.Modal({
32661             cls : 'roo-document-manager-progress-dialog',
32662             allow_close : false,
32663             animate : false,
32664             title : '',
32665             buttons : [
32666                 {
32667                     name  :'cancel',
32668                     weight : 'danger',
32669                     html : 'Cancel'
32670                 }
32671             ], 
32672             listeners : { 
32673                 btnclick : function() {
32674                     _this.uploadCancel();
32675                     this.hide();
32676                 }
32677             }
32678         });
32679          
32680         this.progressDialog.render(Roo.get(document.body));
32681          
32682         this.progress = new Roo.bootstrap.Progress({
32683             cls : 'roo-document-manager-progress',
32684             active : true,
32685             striped : true
32686         });
32687         
32688         this.progress.render(this.progressDialog.getChildContainer());
32689         
32690         this.progressBar = new Roo.bootstrap.ProgressBar({
32691             cls : 'roo-document-manager-progress-bar',
32692             aria_valuenow : 0,
32693             aria_valuemin : 0,
32694             aria_valuemax : 12,
32695             panel : 'success'
32696         });
32697         
32698         this.progressBar.render(this.progress.getChildContainer());
32699     },
32700     
32701     onUploaderClick : function(e)
32702     {
32703         e.preventDefault();
32704      
32705         if(this.fireEvent('beforeselectfile', this) != false){
32706             this.selectorEl.dom.click();
32707         }
32708         
32709     },
32710     
32711     onFileSelected : function(e)
32712     {
32713         e.preventDefault();
32714         
32715         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32716             return;
32717         }
32718         
32719         Roo.each(this.selectorEl.dom.files, function(file){
32720             if(this.fireEvent('inspect', this, file) != false){
32721                 this.files.push(file);
32722             }
32723         }, this);
32724         
32725         this.queue();
32726         
32727     },
32728     
32729     queue : function()
32730     {
32731         this.selectorEl.dom.value = '';
32732         
32733         if(!this.files || !this.files.length){
32734             return;
32735         }
32736         
32737         if(this.boxes > 0 && this.files.length > this.boxes){
32738             this.files = this.files.slice(0, this.boxes);
32739         }
32740         
32741         this.uploader.show();
32742         
32743         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32744             this.uploader.hide();
32745         }
32746         
32747         var _this = this;
32748         
32749         var files = [];
32750         
32751         var docs = [];
32752         
32753         Roo.each(this.files, function(file){
32754             
32755             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32756                 var f = this.renderPreview(file);
32757                 files.push(f);
32758                 return;
32759             }
32760             
32761             if(file.type.indexOf('image') != -1){
32762                 this.delegates.push(
32763                     (function(){
32764                         _this.process(file);
32765                     }).createDelegate(this)
32766                 );
32767         
32768                 return;
32769             }
32770             
32771             docs.push(
32772                 (function(){
32773                     _this.process(file);
32774                 }).createDelegate(this)
32775             );
32776             
32777         }, this);
32778         
32779         this.files = files;
32780         
32781         this.delegates = this.delegates.concat(docs);
32782         
32783         if(!this.delegates.length){
32784             this.refresh();
32785             return;
32786         }
32787         
32788         this.progressBar.aria_valuemax = this.delegates.length;
32789         
32790         this.arrange();
32791         
32792         return;
32793     },
32794     
32795     arrange : function()
32796     {
32797         if(!this.delegates.length){
32798             this.progressDialog.hide();
32799             this.refresh();
32800             return;
32801         }
32802         
32803         var delegate = this.delegates.shift();
32804         
32805         this.progressDialog.show();
32806         
32807         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32808         
32809         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32810         
32811         delegate();
32812     },
32813     
32814     refresh : function()
32815     {
32816         this.uploader.show();
32817         
32818         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32819             this.uploader.hide();
32820         }
32821         
32822         Roo.isTouch ? this.closable(false) : this.closable(true);
32823         
32824         this.fireEvent('refresh', this);
32825     },
32826     
32827     onRemove : function(e, el, o)
32828     {
32829         e.preventDefault();
32830         
32831         this.fireEvent('remove', this, o);
32832         
32833     },
32834     
32835     remove : function(o)
32836     {
32837         var files = [];
32838         
32839         Roo.each(this.files, function(file){
32840             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32841                 files.push(file);
32842                 return;
32843             }
32844
32845             o.target.remove();
32846
32847         }, this);
32848         
32849         this.files = files;
32850         
32851         this.refresh();
32852     },
32853     
32854     clear : function()
32855     {
32856         Roo.each(this.files, function(file){
32857             if(!file.target){
32858                 return;
32859             }
32860             
32861             file.target.remove();
32862
32863         }, this);
32864         
32865         this.files = [];
32866         
32867         this.refresh();
32868     },
32869     
32870     onClick : function(e, el, o)
32871     {
32872         e.preventDefault();
32873         
32874         this.fireEvent('click', this, o);
32875         
32876     },
32877     
32878     closable : function(closable)
32879     {
32880         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32881             
32882             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32883             
32884             if(closable){
32885                 el.show();
32886                 return;
32887             }
32888             
32889             el.hide();
32890             
32891         }, this);
32892     },
32893     
32894     xhrOnLoad : function(xhr)
32895     {
32896         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32897             el.remove();
32898         }, this);
32899         
32900         if (xhr.readyState !== 4) {
32901             this.arrange();
32902             this.fireEvent('exception', this, xhr);
32903             return;
32904         }
32905
32906         var response = Roo.decode(xhr.responseText);
32907         
32908         if(!response.success){
32909             this.arrange();
32910             this.fireEvent('exception', this, xhr);
32911             return;
32912         }
32913         
32914         var file = this.renderPreview(response.data);
32915         
32916         this.files.push(file);
32917         
32918         this.arrange();
32919         
32920         this.fireEvent('afterupload', this, xhr);
32921         
32922     },
32923     
32924     xhrOnError : function(xhr)
32925     {
32926         Roo.log('xhr on error');
32927         
32928         var response = Roo.decode(xhr.responseText);
32929           
32930         Roo.log(response);
32931         
32932         this.arrange();
32933     },
32934     
32935     process : function(file)
32936     {
32937         if(this.fireEvent('process', this, file) !== false){
32938             if(this.editable && file.type.indexOf('image') != -1){
32939                 this.fireEvent('edit', this, file);
32940                 return;
32941             }
32942
32943             this.uploadStart(file, false);
32944
32945             return;
32946         }
32947         
32948     },
32949     
32950     uploadStart : function(file, crop)
32951     {
32952         this.xhr = new XMLHttpRequest();
32953         
32954         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32955             this.arrange();
32956             return;
32957         }
32958         
32959         file.xhr = this.xhr;
32960             
32961         this.managerEl.createChild({
32962             tag : 'div',
32963             cls : 'roo-document-manager-loading',
32964             cn : [
32965                 {
32966                     tag : 'div',
32967                     tooltip : file.name,
32968                     cls : 'roo-document-manager-thumb',
32969                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32970                 }
32971             ]
32972
32973         });
32974
32975         this.xhr.open(this.method, this.url, true);
32976         
32977         var headers = {
32978             "Accept": "application/json",
32979             "Cache-Control": "no-cache",
32980             "X-Requested-With": "XMLHttpRequest"
32981         };
32982         
32983         for (var headerName in headers) {
32984             var headerValue = headers[headerName];
32985             if (headerValue) {
32986                 this.xhr.setRequestHeader(headerName, headerValue);
32987             }
32988         }
32989         
32990         var _this = this;
32991         
32992         this.xhr.onload = function()
32993         {
32994             _this.xhrOnLoad(_this.xhr);
32995         }
32996         
32997         this.xhr.onerror = function()
32998         {
32999             _this.xhrOnError(_this.xhr);
33000         }
33001         
33002         var formData = new FormData();
33003
33004         formData.append('returnHTML', 'NO');
33005         
33006         if(crop){
33007             formData.append('crop', crop);
33008         }
33009         
33010         formData.append(this.paramName, file, file.name);
33011         
33012         var options = {
33013             file : file, 
33014             manually : false
33015         };
33016         
33017         if(this.fireEvent('prepare', this, formData, options) != false){
33018             
33019             if(options.manually){
33020                 return;
33021             }
33022             
33023             this.xhr.send(formData);
33024             return;
33025         };
33026         
33027         this.uploadCancel();
33028     },
33029     
33030     uploadCancel : function()
33031     {
33032         if (this.xhr) {
33033             this.xhr.abort();
33034         }
33035         
33036         this.delegates = [];
33037         
33038         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33039             el.remove();
33040         }, this);
33041         
33042         this.arrange();
33043     },
33044     
33045     renderPreview : function(file)
33046     {
33047         if(typeof(file.target) != 'undefined' && file.target){
33048             return file;
33049         }
33050         
33051         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33052         
33053         var previewEl = this.managerEl.createChild({
33054             tag : 'div',
33055             cls : 'roo-document-manager-preview',
33056             cn : [
33057                 {
33058                     tag : 'div',
33059                     tooltip : file[this.toolTipName],
33060                     cls : 'roo-document-manager-thumb',
33061                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33062                 },
33063                 {
33064                     tag : 'button',
33065                     cls : 'close',
33066                     html : '<i class="fa fa-times-circle"></i>'
33067                 }
33068             ]
33069         });
33070
33071         var close = previewEl.select('button.close', true).first();
33072
33073         close.on('click', this.onRemove, this, file);
33074
33075         file.target = previewEl;
33076
33077         var image = previewEl.select('img', true).first();
33078         
33079         var _this = this;
33080         
33081         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33082         
33083         image.on('click', this.onClick, this, file);
33084         
33085         this.fireEvent('previewrendered', this, file);
33086         
33087         return file;
33088         
33089     },
33090     
33091     onPreviewLoad : function(file, image)
33092     {
33093         if(typeof(file.target) == 'undefined' || !file.target){
33094             return;
33095         }
33096         
33097         var width = image.dom.naturalWidth || image.dom.width;
33098         var height = image.dom.naturalHeight || image.dom.height;
33099         
33100         if(!this.previewResize) {
33101             return;
33102         }
33103         
33104         if(width > height){
33105             file.target.addClass('wide');
33106             return;
33107         }
33108         
33109         file.target.addClass('tall');
33110         return;
33111         
33112     },
33113     
33114     uploadFromSource : function(file, crop)
33115     {
33116         this.xhr = new XMLHttpRequest();
33117         
33118         this.managerEl.createChild({
33119             tag : 'div',
33120             cls : 'roo-document-manager-loading',
33121             cn : [
33122                 {
33123                     tag : 'div',
33124                     tooltip : file.name,
33125                     cls : 'roo-document-manager-thumb',
33126                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33127                 }
33128             ]
33129
33130         });
33131
33132         this.xhr.open(this.method, this.url, true);
33133         
33134         var headers = {
33135             "Accept": "application/json",
33136             "Cache-Control": "no-cache",
33137             "X-Requested-With": "XMLHttpRequest"
33138         };
33139         
33140         for (var headerName in headers) {
33141             var headerValue = headers[headerName];
33142             if (headerValue) {
33143                 this.xhr.setRequestHeader(headerName, headerValue);
33144             }
33145         }
33146         
33147         var _this = this;
33148         
33149         this.xhr.onload = function()
33150         {
33151             _this.xhrOnLoad(_this.xhr);
33152         }
33153         
33154         this.xhr.onerror = function()
33155         {
33156             _this.xhrOnError(_this.xhr);
33157         }
33158         
33159         var formData = new FormData();
33160
33161         formData.append('returnHTML', 'NO');
33162         
33163         formData.append('crop', crop);
33164         
33165         if(typeof(file.filename) != 'undefined'){
33166             formData.append('filename', file.filename);
33167         }
33168         
33169         if(typeof(file.mimetype) != 'undefined'){
33170             formData.append('mimetype', file.mimetype);
33171         }
33172         
33173         Roo.log(formData);
33174         
33175         if(this.fireEvent('prepare', this, formData) != false){
33176             this.xhr.send(formData);
33177         };
33178     }
33179 });
33180
33181 /*
33182 * Licence: LGPL
33183 */
33184
33185 /**
33186  * @class Roo.bootstrap.DocumentViewer
33187  * @extends Roo.bootstrap.Component
33188  * Bootstrap DocumentViewer class
33189  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33190  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33191  * 
33192  * @constructor
33193  * Create a new DocumentViewer
33194  * @param {Object} config The config object
33195  */
33196
33197 Roo.bootstrap.DocumentViewer = function(config){
33198     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33199     
33200     this.addEvents({
33201         /**
33202          * @event initial
33203          * Fire after initEvent
33204          * @param {Roo.bootstrap.DocumentViewer} this
33205          */
33206         "initial" : true,
33207         /**
33208          * @event click
33209          * Fire after click
33210          * @param {Roo.bootstrap.DocumentViewer} this
33211          */
33212         "click" : true,
33213         /**
33214          * @event download
33215          * Fire after download button
33216          * @param {Roo.bootstrap.DocumentViewer} this
33217          */
33218         "download" : true,
33219         /**
33220          * @event trash
33221          * Fire after trash button
33222          * @param {Roo.bootstrap.DocumentViewer} this
33223          */
33224         "trash" : true
33225         
33226     });
33227 };
33228
33229 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33230     
33231     showDownload : true,
33232     
33233     showTrash : true,
33234     
33235     getAutoCreate : function()
33236     {
33237         var cfg = {
33238             tag : 'div',
33239             cls : 'roo-document-viewer',
33240             cn : [
33241                 {
33242                     tag : 'div',
33243                     cls : 'roo-document-viewer-body',
33244                     cn : [
33245                         {
33246                             tag : 'div',
33247                             cls : 'roo-document-viewer-thumb',
33248                             cn : [
33249                                 {
33250                                     tag : 'img',
33251                                     cls : 'roo-document-viewer-image'
33252                                 }
33253                             ]
33254                         }
33255                     ]
33256                 },
33257                 {
33258                     tag : 'div',
33259                     cls : 'roo-document-viewer-footer',
33260                     cn : {
33261                         tag : 'div',
33262                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33263                         cn : [
33264                             {
33265                                 tag : 'div',
33266                                 cls : 'btn-group roo-document-viewer-download',
33267                                 cn : [
33268                                     {
33269                                         tag : 'button',
33270                                         cls : 'btn btn-default',
33271                                         html : '<i class="fa fa-download"></i>'
33272                                     }
33273                                 ]
33274                             },
33275                             {
33276                                 tag : 'div',
33277                                 cls : 'btn-group roo-document-viewer-trash',
33278                                 cn : [
33279                                     {
33280                                         tag : 'button',
33281                                         cls : 'btn btn-default',
33282                                         html : '<i class="fa fa-trash"></i>'
33283                                     }
33284                                 ]
33285                             }
33286                         ]
33287                     }
33288                 }
33289             ]
33290         };
33291         
33292         return cfg;
33293     },
33294     
33295     initEvents : function()
33296     {
33297         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33298         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33299         
33300         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33301         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33302         
33303         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33304         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33305         
33306         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33307         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33308         
33309         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33310         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33311         
33312         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33313         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33314         
33315         this.bodyEl.on('click', this.onClick, this);
33316         this.downloadBtn.on('click', this.onDownload, this);
33317         this.trashBtn.on('click', this.onTrash, this);
33318         
33319         this.downloadBtn.hide();
33320         this.trashBtn.hide();
33321         
33322         if(this.showDownload){
33323             this.downloadBtn.show();
33324         }
33325         
33326         if(this.showTrash){
33327             this.trashBtn.show();
33328         }
33329         
33330         if(!this.showDownload && !this.showTrash) {
33331             this.footerEl.hide();
33332         }
33333         
33334     },
33335     
33336     initial : function()
33337     {
33338         this.fireEvent('initial', this);
33339         
33340     },
33341     
33342     onClick : function(e)
33343     {
33344         e.preventDefault();
33345         
33346         this.fireEvent('click', this);
33347     },
33348     
33349     onDownload : function(e)
33350     {
33351         e.preventDefault();
33352         
33353         this.fireEvent('download', this);
33354     },
33355     
33356     onTrash : function(e)
33357     {
33358         e.preventDefault();
33359         
33360         this.fireEvent('trash', this);
33361     }
33362     
33363 });
33364 /*
33365  * - LGPL
33366  *
33367  * FieldLabel
33368  * 
33369  */
33370
33371 /**
33372  * @class Roo.bootstrap.form.FieldLabel
33373  * @extends Roo.bootstrap.Component
33374  * Bootstrap FieldLabel class
33375  * @cfg {String} html contents of the element
33376  * @cfg {String} tag tag of the element default label
33377  * @cfg {String} cls class of the element
33378  * @cfg {String} target label target 
33379  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33380  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33381  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33382  * @cfg {String} iconTooltip default "This field is required"
33383  * @cfg {String} indicatorpos (left|right) default left
33384  * 
33385  * @constructor
33386  * Create a new FieldLabel
33387  * @param {Object} config The config object
33388  */
33389
33390 Roo.bootstrap.form.FieldLabel = function(config){
33391     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33392     
33393     this.addEvents({
33394             /**
33395              * @event invalid
33396              * Fires after the field has been marked as invalid.
33397              * @param {Roo.form.FieldLabel} this
33398              * @param {String} msg The validation message
33399              */
33400             invalid : true,
33401             /**
33402              * @event valid
33403              * Fires after the field has been validated with no errors.
33404              * @param {Roo.form.FieldLabel} this
33405              */
33406             valid : true
33407         });
33408 };
33409
33410 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33411     
33412     tag: 'label',
33413     cls: '',
33414     html: '',
33415     target: '',
33416     allowBlank : true,
33417     invalidClass : 'has-warning',
33418     validClass : 'has-success',
33419     iconTooltip : 'This field is required',
33420     indicatorpos : 'left',
33421     
33422     getAutoCreate : function(){
33423         
33424         var cls = "";
33425         if (!this.allowBlank) {
33426             cls  = "visible";
33427         }
33428         
33429         var cfg = {
33430             tag : this.tag,
33431             cls : 'roo-bootstrap-field-label ' + this.cls,
33432             for : this.target,
33433             cn : [
33434                 {
33435                     tag : 'i',
33436                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33437                     tooltip : this.iconTooltip
33438                 },
33439                 {
33440                     tag : 'span',
33441                     html : this.html
33442                 }
33443             ] 
33444         };
33445         
33446         if(this.indicatorpos == 'right'){
33447             var cfg = {
33448                 tag : this.tag,
33449                 cls : 'roo-bootstrap-field-label ' + this.cls,
33450                 for : this.target,
33451                 cn : [
33452                     {
33453                         tag : 'span',
33454                         html : this.html
33455                     },
33456                     {
33457                         tag : 'i',
33458                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33459                         tooltip : this.iconTooltip
33460                     }
33461                 ] 
33462             };
33463         }
33464         
33465         return cfg;
33466     },
33467     
33468     initEvents: function() 
33469     {
33470         Roo.bootstrap.Element.superclass.initEvents.call(this);
33471         
33472         this.indicator = this.indicatorEl();
33473         
33474         if(this.indicator){
33475             this.indicator.removeClass('visible');
33476             this.indicator.addClass('invisible');
33477         }
33478         
33479         Roo.bootstrap.form.FieldLabel.register(this);
33480     },
33481     
33482     indicatorEl : function()
33483     {
33484         var indicator = this.el.select('i.roo-required-indicator',true).first();
33485         
33486         if(!indicator){
33487             return false;
33488         }
33489         
33490         return indicator;
33491         
33492     },
33493     
33494     /**
33495      * Mark this field as valid
33496      */
33497     markValid : function()
33498     {
33499         if(this.indicator){
33500             this.indicator.removeClass('visible');
33501             this.indicator.addClass('invisible');
33502         }
33503         if (Roo.bootstrap.version == 3) {
33504             this.el.removeClass(this.invalidClass);
33505             this.el.addClass(this.validClass);
33506         } else {
33507             this.el.removeClass('is-invalid');
33508             this.el.addClass('is-valid');
33509         }
33510         
33511         
33512         this.fireEvent('valid', this);
33513     },
33514     
33515     /**
33516      * Mark this field as invalid
33517      * @param {String} msg The validation message
33518      */
33519     markInvalid : function(msg)
33520     {
33521         if(this.indicator){
33522             this.indicator.removeClass('invisible');
33523             this.indicator.addClass('visible');
33524         }
33525           if (Roo.bootstrap.version == 3) {
33526             this.el.removeClass(this.validClass);
33527             this.el.addClass(this.invalidClass);
33528         } else {
33529             this.el.removeClass('is-valid');
33530             this.el.addClass('is-invalid');
33531         }
33532         
33533         
33534         this.fireEvent('invalid', this, msg);
33535     }
33536     
33537    
33538 });
33539
33540 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33541     
33542     groups: {},
33543     
33544      /**
33545     * register a FieldLabel Group
33546     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33547     */
33548     register : function(label)
33549     {
33550         if(this.groups.hasOwnProperty(label.target)){
33551             return;
33552         }
33553      
33554         this.groups[label.target] = label;
33555         
33556     },
33557     /**
33558     * fetch a FieldLabel Group based on the target
33559     * @param {string} target
33560     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33561     */
33562     get: function(target) {
33563         if (typeof(this.groups[target]) == 'undefined') {
33564             return false;
33565         }
33566         
33567         return this.groups[target] ;
33568     }
33569 });
33570
33571  
33572
33573  /*
33574  * - LGPL
33575  *
33576  * page DateSplitField.
33577  * 
33578  */
33579
33580
33581 /**
33582  * @class Roo.bootstrap.form.DateSplitField
33583  * @extends Roo.bootstrap.Component
33584  * Bootstrap DateSplitField class
33585  * @cfg {string} fieldLabel - the label associated
33586  * @cfg {Number} labelWidth set the width of label (0-12)
33587  * @cfg {String} labelAlign (top|left)
33588  * @cfg {Boolean} dayAllowBlank (true|false) default false
33589  * @cfg {Boolean} monthAllowBlank (true|false) default false
33590  * @cfg {Boolean} yearAllowBlank (true|false) default false
33591  * @cfg {string} dayPlaceholder 
33592  * @cfg {string} monthPlaceholder
33593  * @cfg {string} yearPlaceholder
33594  * @cfg {string} dayFormat default 'd'
33595  * @cfg {string} monthFormat default 'm'
33596  * @cfg {string} yearFormat default 'Y'
33597  * @cfg {Number} labellg set the width of label (1-12)
33598  * @cfg {Number} labelmd set the width of label (1-12)
33599  * @cfg {Number} labelsm set the width of label (1-12)
33600  * @cfg {Number} labelxs set the width of label (1-12)
33601
33602  *     
33603  * @constructor
33604  * Create a new DateSplitField
33605  * @param {Object} config The config object
33606  */
33607
33608 Roo.bootstrap.form.DateSplitField = function(config){
33609     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33610     
33611     this.addEvents({
33612         // raw events
33613          /**
33614          * @event years
33615          * getting the data of years
33616          * @param {Roo.bootstrap.form.DateSplitField} this
33617          * @param {Object} years
33618          */
33619         "years" : true,
33620         /**
33621          * @event days
33622          * getting the data of days
33623          * @param {Roo.bootstrap.form.DateSplitField} this
33624          * @param {Object} days
33625          */
33626         "days" : true,
33627         /**
33628          * @event invalid
33629          * Fires after the field has been marked as invalid.
33630          * @param {Roo.form.Field} this
33631          * @param {String} msg The validation message
33632          */
33633         invalid : true,
33634        /**
33635          * @event valid
33636          * Fires after the field has been validated with no errors.
33637          * @param {Roo.form.Field} this
33638          */
33639         valid : true
33640     });
33641 };
33642
33643 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33644     
33645     fieldLabel : '',
33646     labelAlign : 'top',
33647     labelWidth : 3,
33648     dayAllowBlank : false,
33649     monthAllowBlank : false,
33650     yearAllowBlank : false,
33651     dayPlaceholder : '',
33652     monthPlaceholder : '',
33653     yearPlaceholder : '',
33654     dayFormat : 'd',
33655     monthFormat : 'm',
33656     yearFormat : 'Y',
33657     isFormField : true,
33658     labellg : 0,
33659     labelmd : 0,
33660     labelsm : 0,
33661     labelxs : 0,
33662     
33663     getAutoCreate : function()
33664     {
33665         var cfg = {
33666             tag : 'div',
33667             cls : 'row roo-date-split-field-group',
33668             cn : [
33669                 {
33670                     tag : 'input',
33671                     type : 'hidden',
33672                     cls : 'form-hidden-field roo-date-split-field-group-value',
33673                     name : this.name
33674                 }
33675             ]
33676         };
33677         
33678         var labelCls = 'col-md-12';
33679         var contentCls = 'col-md-4';
33680         
33681         if(this.fieldLabel){
33682             
33683             var label = {
33684                 tag : 'div',
33685                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33686                 cn : [
33687                     {
33688                         tag : 'label',
33689                         html : this.fieldLabel
33690                     }
33691                 ]
33692             };
33693             
33694             if(this.labelAlign == 'left'){
33695             
33696                 if(this.labelWidth > 12){
33697                     label.style = "width: " + this.labelWidth + 'px';
33698                 }
33699
33700                 if(this.labelWidth < 13 && this.labelmd == 0){
33701                     this.labelmd = this.labelWidth;
33702                 }
33703
33704                 if(this.labellg > 0){
33705                     labelCls = ' col-lg-' + this.labellg;
33706                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33707                 }
33708
33709                 if(this.labelmd > 0){
33710                     labelCls = ' col-md-' + this.labelmd;
33711                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33712                 }
33713
33714                 if(this.labelsm > 0){
33715                     labelCls = ' col-sm-' + this.labelsm;
33716                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33717                 }
33718
33719                 if(this.labelxs > 0){
33720                     labelCls = ' col-xs-' + this.labelxs;
33721                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33722                 }
33723             }
33724             
33725             label.cls += ' ' + labelCls;
33726             
33727             cfg.cn.push(label);
33728         }
33729         
33730         Roo.each(['day', 'month', 'year'], function(t){
33731             cfg.cn.push({
33732                 tag : 'div',
33733                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33734             });
33735         }, this);
33736         
33737         return cfg;
33738     },
33739     
33740     inputEl: function ()
33741     {
33742         return this.el.select('.roo-date-split-field-group-value', true).first();
33743     },
33744     
33745     onRender : function(ct, position) 
33746     {
33747         var _this = this;
33748         
33749         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33750         
33751         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33752         
33753         this.dayField = new Roo.bootstrap.form.ComboBox({
33754             allowBlank : this.dayAllowBlank,
33755             alwaysQuery : true,
33756             displayField : 'value',
33757             editable : false,
33758             fieldLabel : '',
33759             forceSelection : true,
33760             mode : 'local',
33761             placeholder : this.dayPlaceholder,
33762             selectOnFocus : true,
33763             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33764             triggerAction : 'all',
33765             typeAhead : true,
33766             valueField : 'value',
33767             store : new Roo.data.SimpleStore({
33768                 data : (function() {    
33769                     var days = [];
33770                     _this.fireEvent('days', _this, days);
33771                     return days;
33772                 })(),
33773                 fields : [ 'value' ]
33774             }),
33775             listeners : {
33776                 select : function (_self, record, index)
33777                 {
33778                     _this.setValue(_this.getValue());
33779                 }
33780             }
33781         });
33782
33783         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33784         
33785         this.monthField = new Roo.bootstrap.form.MonthField({
33786             after : '<i class=\"fa fa-calendar\"></i>',
33787             allowBlank : this.monthAllowBlank,
33788             placeholder : this.monthPlaceholder,
33789             readOnly : true,
33790             listeners : {
33791                 render : function (_self)
33792                 {
33793                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33794                         e.preventDefault();
33795                         _self.focus();
33796                     });
33797                 },
33798                 select : function (_self, oldvalue, newvalue)
33799                 {
33800                     _this.setValue(_this.getValue());
33801                 }
33802             }
33803         });
33804         
33805         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33806         
33807         this.yearField = new Roo.bootstrap.form.ComboBox({
33808             allowBlank : this.yearAllowBlank,
33809             alwaysQuery : true,
33810             displayField : 'value',
33811             editable : false,
33812             fieldLabel : '',
33813             forceSelection : true,
33814             mode : 'local',
33815             placeholder : this.yearPlaceholder,
33816             selectOnFocus : true,
33817             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33818             triggerAction : 'all',
33819             typeAhead : true,
33820             valueField : 'value',
33821             store : new Roo.data.SimpleStore({
33822                 data : (function() {
33823                     var years = [];
33824                     _this.fireEvent('years', _this, years);
33825                     return years;
33826                 })(),
33827                 fields : [ 'value' ]
33828             }),
33829             listeners : {
33830                 select : function (_self, record, index)
33831                 {
33832                     _this.setValue(_this.getValue());
33833                 }
33834             }
33835         });
33836
33837         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33838     },
33839     
33840     setValue : function(v, format)
33841     {
33842         this.inputEl.dom.value = v;
33843         
33844         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33845         
33846         var d = Date.parseDate(v, f);
33847         
33848         if(!d){
33849             this.validate();
33850             return;
33851         }
33852         
33853         this.setDay(d.format(this.dayFormat));
33854         this.setMonth(d.format(this.monthFormat));
33855         this.setYear(d.format(this.yearFormat));
33856         
33857         this.validate();
33858         
33859         return;
33860     },
33861     
33862     setDay : function(v)
33863     {
33864         this.dayField.setValue(v);
33865         this.inputEl.dom.value = this.getValue();
33866         this.validate();
33867         return;
33868     },
33869     
33870     setMonth : function(v)
33871     {
33872         this.monthField.setValue(v, true);
33873         this.inputEl.dom.value = this.getValue();
33874         this.validate();
33875         return;
33876     },
33877     
33878     setYear : function(v)
33879     {
33880         this.yearField.setValue(v);
33881         this.inputEl.dom.value = this.getValue();
33882         this.validate();
33883         return;
33884     },
33885     
33886     getDay : function()
33887     {
33888         return this.dayField.getValue();
33889     },
33890     
33891     getMonth : function()
33892     {
33893         return this.monthField.getValue();
33894     },
33895     
33896     getYear : function()
33897     {
33898         return this.yearField.getValue();
33899     },
33900     
33901     getValue : function()
33902     {
33903         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33904         
33905         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33906         
33907         return date;
33908     },
33909     
33910     reset : function()
33911     {
33912         this.setDay('');
33913         this.setMonth('');
33914         this.setYear('');
33915         this.inputEl.dom.value = '';
33916         this.validate();
33917         return;
33918     },
33919     
33920     validate : function()
33921     {
33922         var d = this.dayField.validate();
33923         var m = this.monthField.validate();
33924         var y = this.yearField.validate();
33925         
33926         var valid = true;
33927         
33928         if(
33929                 (!this.dayAllowBlank && !d) ||
33930                 (!this.monthAllowBlank && !m) ||
33931                 (!this.yearAllowBlank && !y)
33932         ){
33933             valid = false;
33934         }
33935         
33936         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33937             return valid;
33938         }
33939         
33940         if(valid){
33941             this.markValid();
33942             return valid;
33943         }
33944         
33945         this.markInvalid();
33946         
33947         return valid;
33948     },
33949     
33950     markValid : function()
33951     {
33952         
33953         var label = this.el.select('label', true).first();
33954         var icon = this.el.select('i.fa-star', true).first();
33955
33956         if(label && icon){
33957             icon.remove();
33958         }
33959         
33960         this.fireEvent('valid', this);
33961     },
33962     
33963      /**
33964      * Mark this field as invalid
33965      * @param {String} msg The validation message
33966      */
33967     markInvalid : function(msg)
33968     {
33969         
33970         var label = this.el.select('label', true).first();
33971         var icon = this.el.select('i.fa-star', true).first();
33972
33973         if(label && !icon){
33974             this.el.select('.roo-date-split-field-label', true).createChild({
33975                 tag : 'i',
33976                 cls : 'text-danger fa fa-lg fa-star',
33977                 tooltip : 'This field is required',
33978                 style : 'margin-right:5px;'
33979             }, label, true);
33980         }
33981         
33982         this.fireEvent('invalid', this, msg);
33983     },
33984     
33985     clearInvalid : function()
33986     {
33987         var label = this.el.select('label', true).first();
33988         var icon = this.el.select('i.fa-star', true).first();
33989
33990         if(label && icon){
33991             icon.remove();
33992         }
33993         
33994         this.fireEvent('valid', this);
33995     },
33996     
33997     getName: function()
33998     {
33999         return this.name;
34000     }
34001     
34002 });
34003
34004  
34005
34006 /**
34007  * @class Roo.bootstrap.LayoutMasonry
34008  * @extends Roo.bootstrap.Component
34009  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34010  * Bootstrap Layout Masonry class
34011  *
34012  * This is based on 
34013  * http://masonry.desandro.com
34014  *
34015  * The idea is to render all the bricks based on vertical width...
34016  *
34017  * The original code extends 'outlayer' - we might need to use that....
34018
34019  * @constructor
34020  * Create a new Element
34021  * @param {Object} config The config object
34022  */
34023
34024 Roo.bootstrap.LayoutMasonry = function(config){
34025     
34026     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34027     
34028     this.bricks = [];
34029     
34030     Roo.bootstrap.LayoutMasonry.register(this);
34031     
34032     this.addEvents({
34033         // raw events
34034         /**
34035          * @event layout
34036          * Fire after layout the items
34037          * @param {Roo.bootstrap.LayoutMasonry} this
34038          * @param {Roo.EventObject} e
34039          */
34040         "layout" : true
34041     });
34042     
34043 };
34044
34045 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34046     
34047     /**
34048      * @cfg {Boolean} isLayoutInstant = no animation?
34049      */   
34050     isLayoutInstant : false, // needed?
34051    
34052     /**
34053      * @cfg {Number} boxWidth  width of the columns
34054      */   
34055     boxWidth : 450,
34056     
34057       /**
34058      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34059      */   
34060     boxHeight : 0,
34061     
34062     /**
34063      * @cfg {Number} padWidth padding below box..
34064      */   
34065     padWidth : 10, 
34066     
34067     /**
34068      * @cfg {Number} gutter gutter width..
34069      */   
34070     gutter : 10,
34071     
34072      /**
34073      * @cfg {Number} maxCols maximum number of columns
34074      */   
34075     
34076     maxCols: 0,
34077     
34078     /**
34079      * @cfg {Boolean} isAutoInitial defalut true
34080      */   
34081     isAutoInitial : true, 
34082     
34083     containerWidth: 0,
34084     
34085     /**
34086      * @cfg {Boolean} isHorizontal defalut false
34087      */   
34088     isHorizontal : false, 
34089
34090     currentSize : null,
34091     
34092     tag: 'div',
34093     
34094     cls: '',
34095     
34096     bricks: null, //CompositeElement
34097     
34098     cols : 1,
34099     
34100     _isLayoutInited : false,
34101     
34102 //    isAlternative : false, // only use for vertical layout...
34103     
34104     /**
34105      * @cfg {Number} alternativePadWidth padding below box..
34106      */   
34107     alternativePadWidth : 50,
34108     
34109     selectedBrick : [],
34110     
34111     getAutoCreate : function(){
34112         
34113         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34114         
34115         var cfg = {
34116             tag: this.tag,
34117             cls: 'blog-masonary-wrapper ' + this.cls,
34118             cn : {
34119                 cls : 'mas-boxes masonary'
34120             }
34121         };
34122         
34123         return cfg;
34124     },
34125     
34126     getChildContainer: function( )
34127     {
34128         if (this.boxesEl) {
34129             return this.boxesEl;
34130         }
34131         
34132         this.boxesEl = this.el.select('.mas-boxes').first();
34133         
34134         return this.boxesEl;
34135     },
34136     
34137     
34138     initEvents : function()
34139     {
34140         var _this = this;
34141         
34142         if(this.isAutoInitial){
34143             Roo.log('hook children rendered');
34144             this.on('childrenrendered', function() {
34145                 Roo.log('children rendered');
34146                 _this.initial();
34147             } ,this);
34148         }
34149     },
34150     
34151     initial : function()
34152     {
34153         this.selectedBrick = [];
34154         
34155         this.currentSize = this.el.getBox(true);
34156         
34157         Roo.EventManager.onWindowResize(this.resize, this); 
34158
34159         if(!this.isAutoInitial){
34160             this.layout();
34161             return;
34162         }
34163         
34164         this.layout();
34165         
34166         return;
34167         //this.layout.defer(500,this);
34168         
34169     },
34170     
34171     resize : function()
34172     {
34173         var cs = this.el.getBox(true);
34174         
34175         if (
34176                 this.currentSize.width == cs.width && 
34177                 this.currentSize.x == cs.x && 
34178                 this.currentSize.height == cs.height && 
34179                 this.currentSize.y == cs.y 
34180         ) {
34181             Roo.log("no change in with or X or Y");
34182             return;
34183         }
34184         
34185         this.currentSize = cs;
34186         
34187         this.layout();
34188         
34189     },
34190     
34191     layout : function()
34192     {   
34193         this._resetLayout();
34194         
34195         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34196         
34197         this.layoutItems( isInstant );
34198       
34199         this._isLayoutInited = true;
34200         
34201         this.fireEvent('layout', this);
34202         
34203     },
34204     
34205     _resetLayout : function()
34206     {
34207         if(this.isHorizontal){
34208             this.horizontalMeasureColumns();
34209             return;
34210         }
34211         
34212         this.verticalMeasureColumns();
34213         
34214     },
34215     
34216     verticalMeasureColumns : function()
34217     {
34218         this.getContainerWidth();
34219         
34220 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34221 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34222 //            return;
34223 //        }
34224         
34225         var boxWidth = this.boxWidth + this.padWidth;
34226         
34227         if(this.containerWidth < this.boxWidth){
34228             boxWidth = this.containerWidth
34229         }
34230         
34231         var containerWidth = this.containerWidth;
34232         
34233         var cols = Math.floor(containerWidth / boxWidth);
34234         
34235         this.cols = Math.max( cols, 1 );
34236         
34237         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34238         
34239         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34240         
34241         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34242         
34243         this.colWidth = boxWidth + avail - this.padWidth;
34244         
34245         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34246         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34247     },
34248     
34249     horizontalMeasureColumns : function()
34250     {
34251         this.getContainerWidth();
34252         
34253         var boxWidth = this.boxWidth;
34254         
34255         if(this.containerWidth < boxWidth){
34256             boxWidth = this.containerWidth;
34257         }
34258         
34259         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34260         
34261         this.el.setHeight(boxWidth);
34262         
34263     },
34264     
34265     getContainerWidth : function()
34266     {
34267         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34268     },
34269     
34270     layoutItems : function( isInstant )
34271     {
34272         Roo.log(this.bricks);
34273         
34274         var items = Roo.apply([], this.bricks);
34275         
34276         if(this.isHorizontal){
34277             this._horizontalLayoutItems( items , isInstant );
34278             return;
34279         }
34280         
34281 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34282 //            this._verticalAlternativeLayoutItems( items , isInstant );
34283 //            return;
34284 //        }
34285         
34286         this._verticalLayoutItems( items , isInstant );
34287         
34288     },
34289     
34290     _verticalLayoutItems : function ( items , isInstant)
34291     {
34292         if ( !items || !items.length ) {
34293             return;
34294         }
34295         
34296         var standard = [
34297             ['xs', 'xs', 'xs', 'tall'],
34298             ['xs', 'xs', 'tall'],
34299             ['xs', 'xs', 'sm'],
34300             ['xs', 'xs', 'xs'],
34301             ['xs', 'tall'],
34302             ['xs', 'sm'],
34303             ['xs', 'xs'],
34304             ['xs'],
34305             
34306             ['sm', 'xs', 'xs'],
34307             ['sm', 'xs'],
34308             ['sm'],
34309             
34310             ['tall', 'xs', 'xs', 'xs'],
34311             ['tall', 'xs', 'xs'],
34312             ['tall', 'xs'],
34313             ['tall']
34314             
34315         ];
34316         
34317         var queue = [];
34318         
34319         var boxes = [];
34320         
34321         var box = [];
34322         
34323         Roo.each(items, function(item, k){
34324             
34325             switch (item.size) {
34326                 // these layouts take up a full box,
34327                 case 'md' :
34328                 case 'md-left' :
34329                 case 'md-right' :
34330                 case 'wide' :
34331                     
34332                     if(box.length){
34333                         boxes.push(box);
34334                         box = [];
34335                     }
34336                     
34337                     boxes.push([item]);
34338                     
34339                     break;
34340                     
34341                 case 'xs' :
34342                 case 'sm' :
34343                 case 'tall' :
34344                     
34345                     box.push(item);
34346                     
34347                     break;
34348                 default :
34349                     break;
34350                     
34351             }
34352             
34353         }, this);
34354         
34355         if(box.length){
34356             boxes.push(box);
34357             box = [];
34358         }
34359         
34360         var filterPattern = function(box, length)
34361         {
34362             if(!box.length){
34363                 return;
34364             }
34365             
34366             var match = false;
34367             
34368             var pattern = box.slice(0, length);
34369             
34370             var format = [];
34371             
34372             Roo.each(pattern, function(i){
34373                 format.push(i.size);
34374             }, this);
34375             
34376             Roo.each(standard, function(s){
34377                 
34378                 if(String(s) != String(format)){
34379                     return;
34380                 }
34381                 
34382                 match = true;
34383                 return false;
34384                 
34385             }, this);
34386             
34387             if(!match && length == 1){
34388                 return;
34389             }
34390             
34391             if(!match){
34392                 filterPattern(box, length - 1);
34393                 return;
34394             }
34395                 
34396             queue.push(pattern);
34397
34398             box = box.slice(length, box.length);
34399
34400             filterPattern(box, 4);
34401
34402             return;
34403             
34404         }
34405         
34406         Roo.each(boxes, function(box, k){
34407             
34408             if(!box.length){
34409                 return;
34410             }
34411             
34412             if(box.length == 1){
34413                 queue.push(box);
34414                 return;
34415             }
34416             
34417             filterPattern(box, 4);
34418             
34419         }, this);
34420         
34421         this._processVerticalLayoutQueue( queue, isInstant );
34422         
34423     },
34424     
34425 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34426 //    {
34427 //        if ( !items || !items.length ) {
34428 //            return;
34429 //        }
34430 //
34431 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34432 //        
34433 //    },
34434     
34435     _horizontalLayoutItems : function ( items , isInstant)
34436     {
34437         if ( !items || !items.length || items.length < 3) {
34438             return;
34439         }
34440         
34441         items.reverse();
34442         
34443         var eItems = items.slice(0, 3);
34444         
34445         items = items.slice(3, items.length);
34446         
34447         var standard = [
34448             ['xs', 'xs', 'xs', 'wide'],
34449             ['xs', 'xs', 'wide'],
34450             ['xs', 'xs', 'sm'],
34451             ['xs', 'xs', 'xs'],
34452             ['xs', 'wide'],
34453             ['xs', 'sm'],
34454             ['xs', 'xs'],
34455             ['xs'],
34456             
34457             ['sm', 'xs', 'xs'],
34458             ['sm', 'xs'],
34459             ['sm'],
34460             
34461             ['wide', 'xs', 'xs', 'xs'],
34462             ['wide', 'xs', 'xs'],
34463             ['wide', 'xs'],
34464             ['wide'],
34465             
34466             ['wide-thin']
34467         ];
34468         
34469         var queue = [];
34470         
34471         var boxes = [];
34472         
34473         var box = [];
34474         
34475         Roo.each(items, function(item, k){
34476             
34477             switch (item.size) {
34478                 case 'md' :
34479                 case 'md-left' :
34480                 case 'md-right' :
34481                 case 'tall' :
34482                     
34483                     if(box.length){
34484                         boxes.push(box);
34485                         box = [];
34486                     }
34487                     
34488                     boxes.push([item]);
34489                     
34490                     break;
34491                     
34492                 case 'xs' :
34493                 case 'sm' :
34494                 case 'wide' :
34495                 case 'wide-thin' :
34496                     
34497                     box.push(item);
34498                     
34499                     break;
34500                 default :
34501                     break;
34502                     
34503             }
34504             
34505         }, this);
34506         
34507         if(box.length){
34508             boxes.push(box);
34509             box = [];
34510         }
34511         
34512         var filterPattern = function(box, length)
34513         {
34514             if(!box.length){
34515                 return;
34516             }
34517             
34518             var match = false;
34519             
34520             var pattern = box.slice(0, length);
34521             
34522             var format = [];
34523             
34524             Roo.each(pattern, function(i){
34525                 format.push(i.size);
34526             }, this);
34527             
34528             Roo.each(standard, function(s){
34529                 
34530                 if(String(s) != String(format)){
34531                     return;
34532                 }
34533                 
34534                 match = true;
34535                 return false;
34536                 
34537             }, this);
34538             
34539             if(!match && length == 1){
34540                 return;
34541             }
34542             
34543             if(!match){
34544                 filterPattern(box, length - 1);
34545                 return;
34546             }
34547                 
34548             queue.push(pattern);
34549
34550             box = box.slice(length, box.length);
34551
34552             filterPattern(box, 4);
34553
34554             return;
34555             
34556         }
34557         
34558         Roo.each(boxes, function(box, k){
34559             
34560             if(!box.length){
34561                 return;
34562             }
34563             
34564             if(box.length == 1){
34565                 queue.push(box);
34566                 return;
34567             }
34568             
34569             filterPattern(box, 4);
34570             
34571         }, this);
34572         
34573         
34574         var prune = [];
34575         
34576         var pos = this.el.getBox(true);
34577         
34578         var minX = pos.x;
34579         
34580         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34581         
34582         var hit_end = false;
34583         
34584         Roo.each(queue, function(box){
34585             
34586             if(hit_end){
34587                 
34588                 Roo.each(box, function(b){
34589                 
34590                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34591                     b.el.hide();
34592
34593                 }, this);
34594
34595                 return;
34596             }
34597             
34598             var mx = 0;
34599             
34600             Roo.each(box, function(b){
34601                 
34602                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34603                 b.el.show();
34604
34605                 mx = Math.max(mx, b.x);
34606                 
34607             }, this);
34608             
34609             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34610             
34611             if(maxX < minX){
34612                 
34613                 Roo.each(box, function(b){
34614                 
34615                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34616                     b.el.hide();
34617                     
34618                 }, this);
34619                 
34620                 hit_end = true;
34621                 
34622                 return;
34623             }
34624             
34625             prune.push(box);
34626             
34627         }, this);
34628         
34629         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34630     },
34631     
34632     /** Sets position of item in DOM
34633     * @param {Element} item
34634     * @param {Number} x - horizontal position
34635     * @param {Number} y - vertical position
34636     * @param {Boolean} isInstant - disables transitions
34637     */
34638     _processVerticalLayoutQueue : function( queue, isInstant )
34639     {
34640         var pos = this.el.getBox(true);
34641         var x = pos.x;
34642         var y = pos.y;
34643         var maxY = [];
34644         
34645         for (var i = 0; i < this.cols; i++){
34646             maxY[i] = pos.y;
34647         }
34648         
34649         Roo.each(queue, function(box, k){
34650             
34651             var col = k % this.cols;
34652             
34653             Roo.each(box, function(b,kk){
34654                 
34655                 b.el.position('absolute');
34656                 
34657                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34658                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34659                 
34660                 if(b.size == 'md-left' || b.size == 'md-right'){
34661                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34662                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34663                 }
34664                 
34665                 b.el.setWidth(width);
34666                 b.el.setHeight(height);
34667                 // iframe?
34668                 b.el.select('iframe',true).setSize(width,height);
34669                 
34670             }, this);
34671             
34672             for (var i = 0; i < this.cols; i++){
34673                 
34674                 if(maxY[i] < maxY[col]){
34675                     col = i;
34676                     continue;
34677                 }
34678                 
34679                 col = Math.min(col, i);
34680                 
34681             }
34682             
34683             x = pos.x + col * (this.colWidth + this.padWidth);
34684             
34685             y = maxY[col];
34686             
34687             var positions = [];
34688             
34689             switch (box.length){
34690                 case 1 :
34691                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34692                     break;
34693                 case 2 :
34694                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34695                     break;
34696                 case 3 :
34697                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34698                     break;
34699                 case 4 :
34700                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34701                     break;
34702                 default :
34703                     break;
34704             }
34705             
34706             Roo.each(box, function(b,kk){
34707                 
34708                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34709                 
34710                 var sz = b.el.getSize();
34711                 
34712                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34713                 
34714             }, this);
34715             
34716         }, this);
34717         
34718         var mY = 0;
34719         
34720         for (var i = 0; i < this.cols; i++){
34721             mY = Math.max(mY, maxY[i]);
34722         }
34723         
34724         this.el.setHeight(mY - pos.y);
34725         
34726     },
34727     
34728 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34729 //    {
34730 //        var pos = this.el.getBox(true);
34731 //        var x = pos.x;
34732 //        var y = pos.y;
34733 //        var maxX = pos.right;
34734 //        
34735 //        var maxHeight = 0;
34736 //        
34737 //        Roo.each(items, function(item, k){
34738 //            
34739 //            var c = k % 2;
34740 //            
34741 //            item.el.position('absolute');
34742 //                
34743 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34744 //
34745 //            item.el.setWidth(width);
34746 //
34747 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34748 //
34749 //            item.el.setHeight(height);
34750 //            
34751 //            if(c == 0){
34752 //                item.el.setXY([x, y], isInstant ? false : true);
34753 //            } else {
34754 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34755 //            }
34756 //            
34757 //            y = y + height + this.alternativePadWidth;
34758 //            
34759 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34760 //            
34761 //        }, this);
34762 //        
34763 //        this.el.setHeight(maxHeight);
34764 //        
34765 //    },
34766     
34767     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34768     {
34769         var pos = this.el.getBox(true);
34770         
34771         var minX = pos.x;
34772         var minY = pos.y;
34773         
34774         var maxX = pos.right;
34775         
34776         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34777         
34778         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34779         
34780         Roo.each(queue, function(box, k){
34781             
34782             Roo.each(box, function(b, kk){
34783                 
34784                 b.el.position('absolute');
34785                 
34786                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34787                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34788                 
34789                 if(b.size == 'md-left' || b.size == 'md-right'){
34790                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34791                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34792                 }
34793                 
34794                 b.el.setWidth(width);
34795                 b.el.setHeight(height);
34796                 
34797             }, this);
34798             
34799             if(!box.length){
34800                 return;
34801             }
34802             
34803             var positions = [];
34804             
34805             switch (box.length){
34806                 case 1 :
34807                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34808                     break;
34809                 case 2 :
34810                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34811                     break;
34812                 case 3 :
34813                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34814                     break;
34815                 case 4 :
34816                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34817                     break;
34818                 default :
34819                     break;
34820             }
34821             
34822             Roo.each(box, function(b,kk){
34823                 
34824                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34825                 
34826                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34827                 
34828             }, this);
34829             
34830         }, this);
34831         
34832     },
34833     
34834     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34835     {
34836         Roo.each(eItems, function(b,k){
34837             
34838             b.size = (k == 0) ? 'sm' : 'xs';
34839             b.x = (k == 0) ? 2 : 1;
34840             b.y = (k == 0) ? 2 : 1;
34841             
34842             b.el.position('absolute');
34843             
34844             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34845                 
34846             b.el.setWidth(width);
34847             
34848             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34849             
34850             b.el.setHeight(height);
34851             
34852         }, this);
34853
34854         var positions = [];
34855         
34856         positions.push({
34857             x : maxX - this.unitWidth * 2 - this.gutter,
34858             y : minY
34859         });
34860         
34861         positions.push({
34862             x : maxX - this.unitWidth,
34863             y : minY + (this.unitWidth + this.gutter) * 2
34864         });
34865         
34866         positions.push({
34867             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34868             y : minY
34869         });
34870         
34871         Roo.each(eItems, function(b,k){
34872             
34873             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34874
34875         }, this);
34876         
34877     },
34878     
34879     getVerticalOneBoxColPositions : function(x, y, box)
34880     {
34881         var pos = [];
34882         
34883         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34884         
34885         if(box[0].size == 'md-left'){
34886             rand = 0;
34887         }
34888         
34889         if(box[0].size == 'md-right'){
34890             rand = 1;
34891         }
34892         
34893         pos.push({
34894             x : x + (this.unitWidth + this.gutter) * rand,
34895             y : y
34896         });
34897         
34898         return pos;
34899     },
34900     
34901     getVerticalTwoBoxColPositions : function(x, y, box)
34902     {
34903         var pos = [];
34904         
34905         if(box[0].size == 'xs'){
34906             
34907             pos.push({
34908                 x : x,
34909                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34910             });
34911
34912             pos.push({
34913                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34914                 y : y
34915             });
34916             
34917             return pos;
34918             
34919         }
34920         
34921         pos.push({
34922             x : x,
34923             y : y
34924         });
34925
34926         pos.push({
34927             x : x + (this.unitWidth + this.gutter) * 2,
34928             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34929         });
34930         
34931         return pos;
34932         
34933     },
34934     
34935     getVerticalThreeBoxColPositions : function(x, y, box)
34936     {
34937         var pos = [];
34938         
34939         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34940             
34941             pos.push({
34942                 x : x,
34943                 y : y
34944             });
34945
34946             pos.push({
34947                 x : x + (this.unitWidth + this.gutter) * 1,
34948                 y : y
34949             });
34950             
34951             pos.push({
34952                 x : x + (this.unitWidth + this.gutter) * 2,
34953                 y : y
34954             });
34955             
34956             return pos;
34957             
34958         }
34959         
34960         if(box[0].size == 'xs' && box[1].size == 'xs'){
34961             
34962             pos.push({
34963                 x : x,
34964                 y : y
34965             });
34966
34967             pos.push({
34968                 x : x,
34969                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34970             });
34971             
34972             pos.push({
34973                 x : x + (this.unitWidth + this.gutter) * 1,
34974                 y : y
34975             });
34976             
34977             return pos;
34978             
34979         }
34980         
34981         pos.push({
34982             x : x,
34983             y : y
34984         });
34985
34986         pos.push({
34987             x : x + (this.unitWidth + this.gutter) * 2,
34988             y : y
34989         });
34990
34991         pos.push({
34992             x : x + (this.unitWidth + this.gutter) * 2,
34993             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34994         });
34995             
34996         return pos;
34997         
34998     },
34999     
35000     getVerticalFourBoxColPositions : function(x, y, box)
35001     {
35002         var pos = [];
35003         
35004         if(box[0].size == 'xs'){
35005             
35006             pos.push({
35007                 x : x,
35008                 y : y
35009             });
35010
35011             pos.push({
35012                 x : x,
35013                 y : y + (this.unitHeight + this.gutter) * 1
35014             });
35015             
35016             pos.push({
35017                 x : x,
35018                 y : y + (this.unitHeight + this.gutter) * 2
35019             });
35020             
35021             pos.push({
35022                 x : x + (this.unitWidth + this.gutter) * 1,
35023                 y : y
35024             });
35025             
35026             return pos;
35027             
35028         }
35029         
35030         pos.push({
35031             x : x,
35032             y : y
35033         });
35034
35035         pos.push({
35036             x : x + (this.unitWidth + this.gutter) * 2,
35037             y : y
35038         });
35039
35040         pos.push({
35041             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35042             y : y + (this.unitHeight + this.gutter) * 1
35043         });
35044
35045         pos.push({
35046             x : x + (this.unitWidth + this.gutter) * 2,
35047             y : y + (this.unitWidth + this.gutter) * 2
35048         });
35049
35050         return pos;
35051         
35052     },
35053     
35054     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35055     {
35056         var pos = [];
35057         
35058         if(box[0].size == 'md-left'){
35059             pos.push({
35060                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35061                 y : minY
35062             });
35063             
35064             return pos;
35065         }
35066         
35067         if(box[0].size == 'md-right'){
35068             pos.push({
35069                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35070                 y : minY + (this.unitWidth + this.gutter) * 1
35071             });
35072             
35073             return pos;
35074         }
35075         
35076         var rand = Math.floor(Math.random() * (4 - box[0].y));
35077         
35078         pos.push({
35079             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35080             y : minY + (this.unitWidth + this.gutter) * rand
35081         });
35082         
35083         return pos;
35084         
35085     },
35086     
35087     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35088     {
35089         var pos = [];
35090         
35091         if(box[0].size == 'xs'){
35092             
35093             pos.push({
35094                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35095                 y : minY
35096             });
35097
35098             pos.push({
35099                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35100                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35101             });
35102             
35103             return pos;
35104             
35105         }
35106         
35107         pos.push({
35108             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35109             y : minY
35110         });
35111
35112         pos.push({
35113             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35114             y : minY + (this.unitWidth + this.gutter) * 2
35115         });
35116         
35117         return pos;
35118         
35119     },
35120     
35121     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35122     {
35123         var pos = [];
35124         
35125         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].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[1].x - this.gutter * (box[1].x - 1),
35134                 y : minY + (this.unitWidth + this.gutter) * 1
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) * 2
35140             });
35141             
35142             return pos;
35143             
35144         }
35145         
35146         if(box[0].size == 'xs' && box[1].size == 'xs'){
35147             
35148             pos.push({
35149                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35150                 y : minY
35151             });
35152
35153             pos.push({
35154                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35155                 y : minY
35156             });
35157             
35158             pos.push({
35159                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35160                 y : minY + (this.unitWidth + this.gutter) * 1
35161             });
35162             
35163             return pos;
35164             
35165         }
35166         
35167         pos.push({
35168             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35169             y : minY
35170         });
35171
35172         pos.push({
35173             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35174             y : minY + (this.unitWidth + this.gutter) * 2
35175         });
35176
35177         pos.push({
35178             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35179             y : minY + (this.unitWidth + this.gutter) * 2
35180         });
35181             
35182         return pos;
35183         
35184     },
35185     
35186     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35187     {
35188         var pos = [];
35189         
35190         if(box[0].size == 'xs'){
35191             
35192             pos.push({
35193                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35194                 y : minY
35195             });
35196
35197             pos.push({
35198                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35199                 y : minY
35200             });
35201             
35202             pos.push({
35203                 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),
35204                 y : minY
35205             });
35206             
35207             pos.push({
35208                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35209                 y : minY + (this.unitWidth + this.gutter) * 1
35210             });
35211             
35212             return pos;
35213             
35214         }
35215         
35216         pos.push({
35217             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35218             y : minY
35219         });
35220         
35221         pos.push({
35222             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35223             y : minY + (this.unitWidth + this.gutter) * 2
35224         });
35225         
35226         pos.push({
35227             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35228             y : minY + (this.unitWidth + this.gutter) * 2
35229         });
35230         
35231         pos.push({
35232             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),
35233             y : minY + (this.unitWidth + this.gutter) * 2
35234         });
35235
35236         return pos;
35237         
35238     },
35239     
35240     /**
35241     * remove a Masonry Brick
35242     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35243     */
35244     removeBrick : function(brick_id)
35245     {
35246         if (!brick_id) {
35247             return;
35248         }
35249         
35250         for (var i = 0; i<this.bricks.length; i++) {
35251             if (this.bricks[i].id == brick_id) {
35252                 this.bricks.splice(i,1);
35253                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35254                 this.initial();
35255             }
35256         }
35257     },
35258     
35259     /**
35260     * adds a Masonry Brick
35261     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35262     */
35263     addBrick : function(cfg)
35264     {
35265         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35266         //this.register(cn);
35267         cn.parentId = this.id;
35268         cn.render(this.el);
35269         return cn;
35270     },
35271     
35272     /**
35273     * register a Masonry Brick
35274     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35275     */
35276     
35277     register : function(brick)
35278     {
35279         this.bricks.push(brick);
35280         brick.masonryId = this.id;
35281     },
35282     
35283     /**
35284     * clear all the Masonry Brick
35285     */
35286     clearAll : function()
35287     {
35288         this.bricks = [];
35289         //this.getChildContainer().dom.innerHTML = "";
35290         this.el.dom.innerHTML = '';
35291     },
35292     
35293     getSelected : function()
35294     {
35295         if (!this.selectedBrick) {
35296             return false;
35297         }
35298         
35299         return this.selectedBrick;
35300     }
35301 });
35302
35303 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35304     
35305     groups: {},
35306      /**
35307     * register a Masonry Layout
35308     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35309     */
35310     
35311     register : function(layout)
35312     {
35313         this.groups[layout.id] = layout;
35314     },
35315     /**
35316     * fetch a  Masonry Layout based on the masonry layout ID
35317     * @param {string} the masonry layout to add
35318     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35319     */
35320     
35321     get: function(layout_id) {
35322         if (typeof(this.groups[layout_id]) == 'undefined') {
35323             return false;
35324         }
35325         return this.groups[layout_id] ;
35326     }
35327     
35328     
35329     
35330 });
35331
35332  
35333
35334  /**
35335  *
35336  * This is based on 
35337  * http://masonry.desandro.com
35338  *
35339  * The idea is to render all the bricks based on vertical width...
35340  *
35341  * The original code extends 'outlayer' - we might need to use that....
35342  * 
35343  */
35344
35345
35346 /**
35347  * @class Roo.bootstrap.LayoutMasonryAuto
35348  * @extends Roo.bootstrap.Component
35349  * Bootstrap Layout Masonry class
35350  * 
35351  * @constructor
35352  * Create a new Element
35353  * @param {Object} config The config object
35354  */
35355
35356 Roo.bootstrap.LayoutMasonryAuto = function(config){
35357     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35358 };
35359
35360 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35361     
35362       /**
35363      * @cfg {Boolean} isFitWidth  - resize the width..
35364      */   
35365     isFitWidth : false,  // options..
35366     /**
35367      * @cfg {Boolean} isOriginLeft = left align?
35368      */   
35369     isOriginLeft : true,
35370     /**
35371      * @cfg {Boolean} isOriginTop = top align?
35372      */   
35373     isOriginTop : false,
35374     /**
35375      * @cfg {Boolean} isLayoutInstant = no animation?
35376      */   
35377     isLayoutInstant : false, // needed?
35378     /**
35379      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35380      */   
35381     isResizingContainer : true,
35382     /**
35383      * @cfg {Number} columnWidth  width of the columns 
35384      */   
35385     
35386     columnWidth : 0,
35387     
35388     /**
35389      * @cfg {Number} maxCols maximum number of columns
35390      */   
35391     
35392     maxCols: 0,
35393     /**
35394      * @cfg {Number} padHeight padding below box..
35395      */   
35396     
35397     padHeight : 10, 
35398     
35399     /**
35400      * @cfg {Boolean} isAutoInitial defalut true
35401      */   
35402     
35403     isAutoInitial : true, 
35404     
35405     // private?
35406     gutter : 0,
35407     
35408     containerWidth: 0,
35409     initialColumnWidth : 0,
35410     currentSize : null,
35411     
35412     colYs : null, // array.
35413     maxY : 0,
35414     padWidth: 10,
35415     
35416     
35417     tag: 'div',
35418     cls: '',
35419     bricks: null, //CompositeElement
35420     cols : 0, // array?
35421     // element : null, // wrapped now this.el
35422     _isLayoutInited : null, 
35423     
35424     
35425     getAutoCreate : function(){
35426         
35427         var cfg = {
35428             tag: this.tag,
35429             cls: 'blog-masonary-wrapper ' + this.cls,
35430             cn : {
35431                 cls : 'mas-boxes masonary'
35432             }
35433         };
35434         
35435         return cfg;
35436     },
35437     
35438     getChildContainer: function( )
35439     {
35440         if (this.boxesEl) {
35441             return this.boxesEl;
35442         }
35443         
35444         this.boxesEl = this.el.select('.mas-boxes').first();
35445         
35446         return this.boxesEl;
35447     },
35448     
35449     
35450     initEvents : function()
35451     {
35452         var _this = this;
35453         
35454         if(this.isAutoInitial){
35455             Roo.log('hook children rendered');
35456             this.on('childrenrendered', function() {
35457                 Roo.log('children rendered');
35458                 _this.initial();
35459             } ,this);
35460         }
35461         
35462     },
35463     
35464     initial : function()
35465     {
35466         this.reloadItems();
35467
35468         this.currentSize = this.el.getBox(true);
35469
35470         /// was window resize... - let's see if this works..
35471         Roo.EventManager.onWindowResize(this.resize, this); 
35472
35473         if(!this.isAutoInitial){
35474             this.layout();
35475             return;
35476         }
35477         
35478         this.layout.defer(500,this);
35479     },
35480     
35481     reloadItems: function()
35482     {
35483         this.bricks = this.el.select('.masonry-brick', true);
35484         
35485         this.bricks.each(function(b) {
35486             //Roo.log(b.getSize());
35487             if (!b.attr('originalwidth')) {
35488                 b.attr('originalwidth',  b.getSize().width);
35489             }
35490             
35491         });
35492         
35493         Roo.log(this.bricks.elements.length);
35494     },
35495     
35496     resize : function()
35497     {
35498         Roo.log('resize');
35499         var cs = this.el.getBox(true);
35500         
35501         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35502             Roo.log("no change in with or X");
35503             return;
35504         }
35505         this.currentSize = cs;
35506         this.layout();
35507     },
35508     
35509     layout : function()
35510     {
35511          Roo.log('layout');
35512         this._resetLayout();
35513         //this._manageStamps();
35514       
35515         // don't animate first layout
35516         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35517         this.layoutItems( isInstant );
35518       
35519         // flag for initalized
35520         this._isLayoutInited = true;
35521     },
35522     
35523     layoutItems : function( isInstant )
35524     {
35525         //var items = this._getItemsForLayout( this.items );
35526         // original code supports filtering layout items.. we just ignore it..
35527         
35528         this._layoutItems( this.bricks , isInstant );
35529       
35530         this._postLayout();
35531     },
35532     _layoutItems : function ( items , isInstant)
35533     {
35534        //this.fireEvent( 'layout', this, items );
35535     
35536
35537         if ( !items || !items.elements.length ) {
35538           // no items, emit event with empty array
35539             return;
35540         }
35541
35542         var queue = [];
35543         items.each(function(item) {
35544             Roo.log("layout item");
35545             Roo.log(item);
35546             // get x/y object from method
35547             var position = this._getItemLayoutPosition( item );
35548             // enqueue
35549             position.item = item;
35550             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35551             queue.push( position );
35552         }, this);
35553       
35554         this._processLayoutQueue( queue );
35555     },
35556     /** Sets position of item in DOM
35557     * @param {Element} item
35558     * @param {Number} x - horizontal position
35559     * @param {Number} y - vertical position
35560     * @param {Boolean} isInstant - disables transitions
35561     */
35562     _processLayoutQueue : function( queue )
35563     {
35564         for ( var i=0, len = queue.length; i < len; i++ ) {
35565             var obj = queue[i];
35566             obj.item.position('absolute');
35567             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35568         }
35569     },
35570       
35571     
35572     /**
35573     * Any logic you want to do after each layout,
35574     * i.e. size the container
35575     */
35576     _postLayout : function()
35577     {
35578         this.resizeContainer();
35579     },
35580     
35581     resizeContainer : function()
35582     {
35583         if ( !this.isResizingContainer ) {
35584             return;
35585         }
35586         var size = this._getContainerSize();
35587         if ( size ) {
35588             this.el.setSize(size.width,size.height);
35589             this.boxesEl.setSize(size.width,size.height);
35590         }
35591     },
35592     
35593     
35594     
35595     _resetLayout : function()
35596     {
35597         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35598         this.colWidth = this.el.getWidth();
35599         //this.gutter = this.el.getWidth(); 
35600         
35601         this.measureColumns();
35602
35603         // reset column Y
35604         var i = this.cols;
35605         this.colYs = [];
35606         while (i--) {
35607             this.colYs.push( 0 );
35608         }
35609     
35610         this.maxY = 0;
35611     },
35612
35613     measureColumns : function()
35614     {
35615         this.getContainerWidth();
35616       // if columnWidth is 0, default to outerWidth of first item
35617         if ( !this.columnWidth ) {
35618             var firstItem = this.bricks.first();
35619             Roo.log(firstItem);
35620             this.columnWidth  = this.containerWidth;
35621             if (firstItem && firstItem.attr('originalwidth') ) {
35622                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35623             }
35624             // columnWidth fall back to item of first element
35625             Roo.log("set column width?");
35626                         this.initialColumnWidth = this.columnWidth  ;
35627
35628             // if first elem has no width, default to size of container
35629             
35630         }
35631         
35632         
35633         if (this.initialColumnWidth) {
35634             this.columnWidth = this.initialColumnWidth;
35635         }
35636         
35637         
35638             
35639         // column width is fixed at the top - however if container width get's smaller we should
35640         // reduce it...
35641         
35642         // this bit calcs how man columns..
35643             
35644         var columnWidth = this.columnWidth += this.gutter;
35645       
35646         // calculate columns
35647         var containerWidth = this.containerWidth + this.gutter;
35648         
35649         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35650         // fix rounding errors, typically with gutters
35651         var excess = columnWidth - containerWidth % columnWidth;
35652         
35653         
35654         // if overshoot is less than a pixel, round up, otherwise floor it
35655         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35656         cols = Math[ mathMethod ]( cols );
35657         this.cols = Math.max( cols, 1 );
35658         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35659         
35660          // padding positioning..
35661         var totalColWidth = this.cols * this.columnWidth;
35662         var padavail = this.containerWidth - totalColWidth;
35663         // so for 2 columns - we need 3 'pads'
35664         
35665         var padNeeded = (1+this.cols) * this.padWidth;
35666         
35667         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35668         
35669         this.columnWidth += padExtra
35670         //this.padWidth = Math.floor(padavail /  ( this.cols));
35671         
35672         // adjust colum width so that padding is fixed??
35673         
35674         // we have 3 columns ... total = width * 3
35675         // we have X left over... that should be used by 
35676         
35677         //if (this.expandC) {
35678             
35679         //}
35680         
35681         
35682         
35683     },
35684     
35685     getContainerWidth : function()
35686     {
35687        /* // container is parent if fit width
35688         var container = this.isFitWidth ? this.element.parentNode : this.element;
35689         // check that this.size and size are there
35690         // IE8 triggers resize on body size change, so they might not be
35691         
35692         var size = getSize( container );  //FIXME
35693         this.containerWidth = size && size.innerWidth; //FIXME
35694         */
35695          
35696         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35697         
35698     },
35699     
35700     _getItemLayoutPosition : function( item )  // what is item?
35701     {
35702         // we resize the item to our columnWidth..
35703       
35704         item.setWidth(this.columnWidth);
35705         item.autoBoxAdjust  = false;
35706         
35707         var sz = item.getSize();
35708  
35709         // how many columns does this brick span
35710         var remainder = this.containerWidth % this.columnWidth;
35711         
35712         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35713         // round if off by 1 pixel, otherwise use ceil
35714         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35715         colSpan = Math.min( colSpan, this.cols );
35716         
35717         // normally this should be '1' as we dont' currently allow multi width columns..
35718         
35719         var colGroup = this._getColGroup( colSpan );
35720         // get the minimum Y value from the columns
35721         var minimumY = Math.min.apply( Math, colGroup );
35722         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35723         
35724         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35725          
35726         // position the brick
35727         var position = {
35728             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35729             y: this.currentSize.y + minimumY + this.padHeight
35730         };
35731         
35732         Roo.log(position);
35733         // apply setHeight to necessary columns
35734         var setHeight = minimumY + sz.height + this.padHeight;
35735         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35736         
35737         var setSpan = this.cols + 1 - colGroup.length;
35738         for ( var i = 0; i < setSpan; i++ ) {
35739           this.colYs[ shortColIndex + i ] = setHeight ;
35740         }
35741       
35742         return position;
35743     },
35744     
35745     /**
35746      * @param {Number} colSpan - number of columns the element spans
35747      * @returns {Array} colGroup
35748      */
35749     _getColGroup : function( colSpan )
35750     {
35751         if ( colSpan < 2 ) {
35752           // if brick spans only one column, use all the column Ys
35753           return this.colYs;
35754         }
35755       
35756         var colGroup = [];
35757         // how many different places could this brick fit horizontally
35758         var groupCount = this.cols + 1 - colSpan;
35759         // for each group potential horizontal position
35760         for ( var i = 0; i < groupCount; i++ ) {
35761           // make an array of colY values for that one group
35762           var groupColYs = this.colYs.slice( i, i + colSpan );
35763           // and get the max value of the array
35764           colGroup[i] = Math.max.apply( Math, groupColYs );
35765         }
35766         return colGroup;
35767     },
35768     /*
35769     _manageStamp : function( stamp )
35770     {
35771         var stampSize =  stamp.getSize();
35772         var offset = stamp.getBox();
35773         // get the columns that this stamp affects
35774         var firstX = this.isOriginLeft ? offset.x : offset.right;
35775         var lastX = firstX + stampSize.width;
35776         var firstCol = Math.floor( firstX / this.columnWidth );
35777         firstCol = Math.max( 0, firstCol );
35778         
35779         var lastCol = Math.floor( lastX / this.columnWidth );
35780         // lastCol should not go over if multiple of columnWidth #425
35781         lastCol -= lastX % this.columnWidth ? 0 : 1;
35782         lastCol = Math.min( this.cols - 1, lastCol );
35783         
35784         // set colYs to bottom of the stamp
35785         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35786             stampSize.height;
35787             
35788         for ( var i = firstCol; i <= lastCol; i++ ) {
35789           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35790         }
35791     },
35792     */
35793     
35794     _getContainerSize : function()
35795     {
35796         this.maxY = Math.max.apply( Math, this.colYs );
35797         var size = {
35798             height: this.maxY
35799         };
35800       
35801         if ( this.isFitWidth ) {
35802             size.width = this._getContainerFitWidth();
35803         }
35804       
35805         return size;
35806     },
35807     
35808     _getContainerFitWidth : function()
35809     {
35810         var unusedCols = 0;
35811         // count unused columns
35812         var i = this.cols;
35813         while ( --i ) {
35814           if ( this.colYs[i] !== 0 ) {
35815             break;
35816           }
35817           unusedCols++;
35818         }
35819         // fit container to columns that have been used
35820         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35821     },
35822     
35823     needsResizeLayout : function()
35824     {
35825         var previousWidth = this.containerWidth;
35826         this.getContainerWidth();
35827         return previousWidth !== this.containerWidth;
35828     }
35829  
35830 });
35831
35832  
35833
35834  /*
35835  * - LGPL
35836  *
35837  * element
35838  * 
35839  */
35840
35841 /**
35842  * @class Roo.bootstrap.MasonryBrick
35843  * @extends Roo.bootstrap.Component
35844  * Bootstrap MasonryBrick class
35845  * 
35846  * @constructor
35847  * Create a new MasonryBrick
35848  * @param {Object} config The config object
35849  */
35850
35851 Roo.bootstrap.MasonryBrick = function(config){
35852     
35853     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35854     
35855     Roo.bootstrap.MasonryBrick.register(this);
35856     
35857     this.addEvents({
35858         // raw events
35859         /**
35860          * @event click
35861          * When a MasonryBrick is clcik
35862          * @param {Roo.bootstrap.MasonryBrick} this
35863          * @param {Roo.EventObject} e
35864          */
35865         "click" : true
35866     });
35867 };
35868
35869 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35870     
35871     /**
35872      * @cfg {String} title
35873      */   
35874     title : '',
35875     /**
35876      * @cfg {String} html
35877      */   
35878     html : '',
35879     /**
35880      * @cfg {String} bgimage
35881      */   
35882     bgimage : '',
35883     /**
35884      * @cfg {String} videourl
35885      */   
35886     videourl : '',
35887     /**
35888      * @cfg {String} cls
35889      */   
35890     cls : '',
35891     /**
35892      * @cfg {String} href
35893      */   
35894     href : '',
35895     /**
35896      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35897      */   
35898     size : 'xs',
35899     
35900     /**
35901      * @cfg {String} placetitle (center|bottom)
35902      */   
35903     placetitle : '',
35904     
35905     /**
35906      * @cfg {Boolean} isFitContainer defalut true
35907      */   
35908     isFitContainer : true, 
35909     
35910     /**
35911      * @cfg {Boolean} preventDefault defalut false
35912      */   
35913     preventDefault : false, 
35914     
35915     /**
35916      * @cfg {Boolean} inverse defalut false
35917      */   
35918     maskInverse : false, 
35919     
35920     getAutoCreate : function()
35921     {
35922         if(!this.isFitContainer){
35923             return this.getSplitAutoCreate();
35924         }
35925         
35926         var cls = 'masonry-brick masonry-brick-full';
35927         
35928         if(this.href.length){
35929             cls += ' masonry-brick-link';
35930         }
35931         
35932         if(this.bgimage.length){
35933             cls += ' masonry-brick-image';
35934         }
35935         
35936         if(this.maskInverse){
35937             cls += ' mask-inverse';
35938         }
35939         
35940         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35941             cls += ' enable-mask';
35942         }
35943         
35944         if(this.size){
35945             cls += ' masonry-' + this.size + '-brick';
35946         }
35947         
35948         if(this.placetitle.length){
35949             
35950             switch (this.placetitle) {
35951                 case 'center' :
35952                     cls += ' masonry-center-title';
35953                     break;
35954                 case 'bottom' :
35955                     cls += ' masonry-bottom-title';
35956                     break;
35957                 default:
35958                     break;
35959             }
35960             
35961         } else {
35962             if(!this.html.length && !this.bgimage.length){
35963                 cls += ' masonry-center-title';
35964             }
35965
35966             if(!this.html.length && this.bgimage.length){
35967                 cls += ' masonry-bottom-title';
35968             }
35969         }
35970         
35971         if(this.cls){
35972             cls += ' ' + this.cls;
35973         }
35974         
35975         var cfg = {
35976             tag: (this.href.length) ? 'a' : 'div',
35977             cls: cls,
35978             cn: [
35979                 {
35980                     tag: 'div',
35981                     cls: 'masonry-brick-mask'
35982                 },
35983                 {
35984                     tag: 'div',
35985                     cls: 'masonry-brick-paragraph',
35986                     cn: []
35987                 }
35988             ]
35989         };
35990         
35991         if(this.href.length){
35992             cfg.href = this.href;
35993         }
35994         
35995         var cn = cfg.cn[1].cn;
35996         
35997         if(this.title.length){
35998             cn.push({
35999                 tag: 'h4',
36000                 cls: 'masonry-brick-title',
36001                 html: this.title
36002             });
36003         }
36004         
36005         if(this.html.length){
36006             cn.push({
36007                 tag: 'p',
36008                 cls: 'masonry-brick-text',
36009                 html: this.html
36010             });
36011         }
36012         
36013         if (!this.title.length && !this.html.length) {
36014             cfg.cn[1].cls += ' hide';
36015         }
36016         
36017         if(this.bgimage.length){
36018             cfg.cn.push({
36019                 tag: 'img',
36020                 cls: 'masonry-brick-image-view',
36021                 src: this.bgimage
36022             });
36023         }
36024         
36025         if(this.videourl.length){
36026             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36027             // youtube support only?
36028             cfg.cn.push({
36029                 tag: 'iframe',
36030                 cls: 'masonry-brick-image-view',
36031                 src: vurl,
36032                 frameborder : 0,
36033                 allowfullscreen : true
36034             });
36035         }
36036         
36037         return cfg;
36038         
36039     },
36040     
36041     getSplitAutoCreate : function()
36042     {
36043         var cls = 'masonry-brick masonry-brick-split';
36044         
36045         if(this.href.length){
36046             cls += ' masonry-brick-link';
36047         }
36048         
36049         if(this.bgimage.length){
36050             cls += ' masonry-brick-image';
36051         }
36052         
36053         if(this.size){
36054             cls += ' masonry-' + this.size + '-brick';
36055         }
36056         
36057         switch (this.placetitle) {
36058             case 'center' :
36059                 cls += ' masonry-center-title';
36060                 break;
36061             case 'bottom' :
36062                 cls += ' masonry-bottom-title';
36063                 break;
36064             default:
36065                 if(!this.bgimage.length){
36066                     cls += ' masonry-center-title';
36067                 }
36068
36069                 if(this.bgimage.length){
36070                     cls += ' masonry-bottom-title';
36071                 }
36072                 break;
36073         }
36074         
36075         if(this.cls){
36076             cls += ' ' + this.cls;
36077         }
36078         
36079         var cfg = {
36080             tag: (this.href.length) ? 'a' : 'div',
36081             cls: cls,
36082             cn: [
36083                 {
36084                     tag: 'div',
36085                     cls: 'masonry-brick-split-head',
36086                     cn: [
36087                         {
36088                             tag: 'div',
36089                             cls: 'masonry-brick-paragraph',
36090                             cn: []
36091                         }
36092                     ]
36093                 },
36094                 {
36095                     tag: 'div',
36096                     cls: 'masonry-brick-split-body',
36097                     cn: []
36098                 }
36099             ]
36100         };
36101         
36102         if(this.href.length){
36103             cfg.href = this.href;
36104         }
36105         
36106         if(this.title.length){
36107             cfg.cn[0].cn[0].cn.push({
36108                 tag: 'h4',
36109                 cls: 'masonry-brick-title',
36110                 html: this.title
36111             });
36112         }
36113         
36114         if(this.html.length){
36115             cfg.cn[1].cn.push({
36116                 tag: 'p',
36117                 cls: 'masonry-brick-text',
36118                 html: this.html
36119             });
36120         }
36121
36122         if(this.bgimage.length){
36123             cfg.cn[0].cn.push({
36124                 tag: 'img',
36125                 cls: 'masonry-brick-image-view',
36126                 src: this.bgimage
36127             });
36128         }
36129         
36130         if(this.videourl.length){
36131             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36132             // youtube support only?
36133             cfg.cn[0].cn.cn.push({
36134                 tag: 'iframe',
36135                 cls: 'masonry-brick-image-view',
36136                 src: vurl,
36137                 frameborder : 0,
36138                 allowfullscreen : true
36139             });
36140         }
36141         
36142         return cfg;
36143     },
36144     
36145     initEvents: function() 
36146     {
36147         switch (this.size) {
36148             case 'xs' :
36149                 this.x = 1;
36150                 this.y = 1;
36151                 break;
36152             case 'sm' :
36153                 this.x = 2;
36154                 this.y = 2;
36155                 break;
36156             case 'md' :
36157             case 'md-left' :
36158             case 'md-right' :
36159                 this.x = 3;
36160                 this.y = 3;
36161                 break;
36162             case 'tall' :
36163                 this.x = 2;
36164                 this.y = 3;
36165                 break;
36166             case 'wide' :
36167                 this.x = 3;
36168                 this.y = 2;
36169                 break;
36170             case 'wide-thin' :
36171                 this.x = 3;
36172                 this.y = 1;
36173                 break;
36174                         
36175             default :
36176                 break;
36177         }
36178         
36179         if(Roo.isTouch){
36180             this.el.on('touchstart', this.onTouchStart, this);
36181             this.el.on('touchmove', this.onTouchMove, this);
36182             this.el.on('touchend', this.onTouchEnd, this);
36183             this.el.on('contextmenu', this.onContextMenu, this);
36184         } else {
36185             this.el.on('mouseenter'  ,this.enter, this);
36186             this.el.on('mouseleave', this.leave, this);
36187             this.el.on('click', this.onClick, this);
36188         }
36189         
36190         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36191             this.parent().bricks.push(this);   
36192         }
36193         
36194     },
36195     
36196     onClick: function(e, el)
36197     {
36198         var time = this.endTimer - this.startTimer;
36199         // Roo.log(e.preventDefault());
36200         if(Roo.isTouch){
36201             if(time > 1000){
36202                 e.preventDefault();
36203                 return;
36204             }
36205         }
36206         
36207         if(!this.preventDefault){
36208             return;
36209         }
36210         
36211         e.preventDefault();
36212         
36213         if (this.activeClass != '') {
36214             this.selectBrick();
36215         }
36216         
36217         this.fireEvent('click', this, e);
36218     },
36219     
36220     enter: function(e, el)
36221     {
36222         e.preventDefault();
36223         
36224         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36225             return;
36226         }
36227         
36228         if(this.bgimage.length && this.html.length){
36229             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36230         }
36231     },
36232     
36233     leave: function(e, el)
36234     {
36235         e.preventDefault();
36236         
36237         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36238             return;
36239         }
36240         
36241         if(this.bgimage.length && this.html.length){
36242             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36243         }
36244     },
36245     
36246     onTouchStart: function(e, el)
36247     {
36248 //        e.preventDefault();
36249         
36250         this.touchmoved = false;
36251         
36252         if(!this.isFitContainer){
36253             return;
36254         }
36255         
36256         if(!this.bgimage.length || !this.html.length){
36257             return;
36258         }
36259         
36260         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36261         
36262         this.timer = new Date().getTime();
36263         
36264     },
36265     
36266     onTouchMove: function(e, el)
36267     {
36268         this.touchmoved = true;
36269     },
36270     
36271     onContextMenu : function(e,el)
36272     {
36273         e.preventDefault();
36274         e.stopPropagation();
36275         return false;
36276     },
36277     
36278     onTouchEnd: function(e, el)
36279     {
36280 //        e.preventDefault();
36281         
36282         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36283         
36284             this.leave(e,el);
36285             
36286             return;
36287         }
36288         
36289         if(!this.bgimage.length || !this.html.length){
36290             
36291             if(this.href.length){
36292                 window.location.href = this.href;
36293             }
36294             
36295             return;
36296         }
36297         
36298         if(!this.isFitContainer){
36299             return;
36300         }
36301         
36302         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36303         
36304         window.location.href = this.href;
36305     },
36306     
36307     //selection on single brick only
36308     selectBrick : function() {
36309         
36310         if (!this.parentId) {
36311             return;
36312         }
36313         
36314         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36315         var index = m.selectedBrick.indexOf(this.id);
36316         
36317         if ( index > -1) {
36318             m.selectedBrick.splice(index,1);
36319             this.el.removeClass(this.activeClass);
36320             return;
36321         }
36322         
36323         for(var i = 0; i < m.selectedBrick.length; i++) {
36324             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36325             b.el.removeClass(b.activeClass);
36326         }
36327         
36328         m.selectedBrick = [];
36329         
36330         m.selectedBrick.push(this.id);
36331         this.el.addClass(this.activeClass);
36332         return;
36333     },
36334     
36335     isSelected : function(){
36336         return this.el.hasClass(this.activeClass);
36337         
36338     }
36339 });
36340
36341 Roo.apply(Roo.bootstrap.MasonryBrick, {
36342     
36343     //groups: {},
36344     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36345      /**
36346     * register a Masonry Brick
36347     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36348     */
36349     
36350     register : function(brick)
36351     {
36352         //this.groups[brick.id] = brick;
36353         this.groups.add(brick.id, brick);
36354     },
36355     /**
36356     * fetch a  masonry brick based on the masonry brick ID
36357     * @param {string} the masonry brick to add
36358     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36359     */
36360     
36361     get: function(brick_id) 
36362     {
36363         // if (typeof(this.groups[brick_id]) == 'undefined') {
36364         //     return false;
36365         // }
36366         // return this.groups[brick_id] ;
36367         
36368         if(this.groups.key(brick_id)) {
36369             return this.groups.key(brick_id);
36370         }
36371         
36372         return false;
36373     }
36374     
36375     
36376     
36377 });
36378
36379  /*
36380  * - LGPL
36381  *
36382  * element
36383  * 
36384  */
36385
36386 /**
36387  * @class Roo.bootstrap.Brick
36388  * @extends Roo.bootstrap.Component
36389  * Bootstrap Brick class
36390  * 
36391  * @constructor
36392  * Create a new Brick
36393  * @param {Object} config The config object
36394  */
36395
36396 Roo.bootstrap.Brick = function(config){
36397     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36398     
36399     this.addEvents({
36400         // raw events
36401         /**
36402          * @event click
36403          * When a Brick is click
36404          * @param {Roo.bootstrap.Brick} this
36405          * @param {Roo.EventObject} e
36406          */
36407         "click" : true
36408     });
36409 };
36410
36411 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36412     
36413     /**
36414      * @cfg {String} title
36415      */   
36416     title : '',
36417     /**
36418      * @cfg {String} html
36419      */   
36420     html : '',
36421     /**
36422      * @cfg {String} bgimage
36423      */   
36424     bgimage : '',
36425     /**
36426      * @cfg {String} cls
36427      */   
36428     cls : '',
36429     /**
36430      * @cfg {String} href
36431      */   
36432     href : '',
36433     /**
36434      * @cfg {String} video
36435      */   
36436     video : '',
36437     /**
36438      * @cfg {Boolean} square
36439      */   
36440     square : true,
36441     
36442     getAutoCreate : function()
36443     {
36444         var cls = 'roo-brick';
36445         
36446         if(this.href.length){
36447             cls += ' roo-brick-link';
36448         }
36449         
36450         if(this.bgimage.length){
36451             cls += ' roo-brick-image';
36452         }
36453         
36454         if(!this.html.length && !this.bgimage.length){
36455             cls += ' roo-brick-center-title';
36456         }
36457         
36458         if(!this.html.length && this.bgimage.length){
36459             cls += ' roo-brick-bottom-title';
36460         }
36461         
36462         if(this.cls){
36463             cls += ' ' + this.cls;
36464         }
36465         
36466         var cfg = {
36467             tag: (this.href.length) ? 'a' : 'div',
36468             cls: cls,
36469             cn: [
36470                 {
36471                     tag: 'div',
36472                     cls: 'roo-brick-paragraph',
36473                     cn: []
36474                 }
36475             ]
36476         };
36477         
36478         if(this.href.length){
36479             cfg.href = this.href;
36480         }
36481         
36482         var cn = cfg.cn[0].cn;
36483         
36484         if(this.title.length){
36485             cn.push({
36486                 tag: 'h4',
36487                 cls: 'roo-brick-title',
36488                 html: this.title
36489             });
36490         }
36491         
36492         if(this.html.length){
36493             cn.push({
36494                 tag: 'p',
36495                 cls: 'roo-brick-text',
36496                 html: this.html
36497             });
36498         } else {
36499             cn.cls += ' hide';
36500         }
36501         
36502         if(this.bgimage.length){
36503             cfg.cn.push({
36504                 tag: 'img',
36505                 cls: 'roo-brick-image-view',
36506                 src: this.bgimage
36507             });
36508         }
36509         
36510         return cfg;
36511     },
36512     
36513     initEvents: function() 
36514     {
36515         if(this.title.length || this.html.length){
36516             this.el.on('mouseenter'  ,this.enter, this);
36517             this.el.on('mouseleave', this.leave, this);
36518         }
36519         
36520         Roo.EventManager.onWindowResize(this.resize, this); 
36521         
36522         if(this.bgimage.length){
36523             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36524             this.imageEl.on('load', this.onImageLoad, this);
36525             return;
36526         }
36527         
36528         this.resize();
36529     },
36530     
36531     onImageLoad : function()
36532     {
36533         this.resize();
36534     },
36535     
36536     resize : function()
36537     {
36538         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36539         
36540         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36541         
36542         if(this.bgimage.length){
36543             var image = this.el.select('.roo-brick-image-view', true).first();
36544             
36545             image.setWidth(paragraph.getWidth());
36546             
36547             if(this.square){
36548                 image.setHeight(paragraph.getWidth());
36549             }
36550             
36551             this.el.setHeight(image.getHeight());
36552             paragraph.setHeight(image.getHeight());
36553             
36554         }
36555         
36556     },
36557     
36558     enter: function(e, el)
36559     {
36560         e.preventDefault();
36561         
36562         if(this.bgimage.length){
36563             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36564             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36565         }
36566     },
36567     
36568     leave: function(e, el)
36569     {
36570         e.preventDefault();
36571         
36572         if(this.bgimage.length){
36573             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36574             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36575         }
36576     }
36577     
36578 });
36579
36580  
36581
36582  /*
36583  * - LGPL
36584  *
36585  * Number field 
36586  */
36587
36588 /**
36589  * @class Roo.bootstrap.form.NumberField
36590  * @extends Roo.bootstrap.form.Input
36591  * Bootstrap NumberField class
36592  * 
36593  * 
36594  * 
36595  * 
36596  * @constructor
36597  * Create a new NumberField
36598  * @param {Object} config The config object
36599  */
36600
36601 Roo.bootstrap.form.NumberField = function(config){
36602     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36603 };
36604
36605 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36606     
36607     /**
36608      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36609      */
36610     allowDecimals : true,
36611     /**
36612      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36613      */
36614     decimalSeparator : ".",
36615     /**
36616      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36617      */
36618     decimalPrecision : 2,
36619     /**
36620      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36621      */
36622     allowNegative : true,
36623     
36624     /**
36625      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36626      */
36627     allowZero: true,
36628     /**
36629      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36630      */
36631     minValue : Number.NEGATIVE_INFINITY,
36632     /**
36633      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36634      */
36635     maxValue : Number.MAX_VALUE,
36636     /**
36637      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36638      */
36639     minText : "The minimum value for this field is {0}",
36640     /**
36641      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36642      */
36643     maxText : "The maximum value for this field is {0}",
36644     /**
36645      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36646      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36647      */
36648     nanText : "{0} is not a valid number",
36649     /**
36650      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36651      */
36652     thousandsDelimiter : false,
36653     /**
36654      * @cfg {String} valueAlign alignment of value
36655      */
36656     valueAlign : "left",
36657
36658     getAutoCreate : function()
36659     {
36660         var hiddenInput = {
36661             tag: 'input',
36662             type: 'hidden',
36663             id: Roo.id(),
36664             cls: 'hidden-number-input'
36665         };
36666         
36667         if (this.name) {
36668             hiddenInput.name = this.name;
36669         }
36670         
36671         this.name = '';
36672         
36673         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36674         
36675         this.name = hiddenInput.name;
36676         
36677         if(cfg.cn.length > 0) {
36678             cfg.cn.push(hiddenInput);
36679         }
36680         
36681         return cfg;
36682     },
36683
36684     // private
36685     initEvents : function()
36686     {   
36687         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36688         
36689         var allowed = "0123456789";
36690         
36691         if(this.allowDecimals){
36692             allowed += this.decimalSeparator;
36693         }
36694         
36695         if(this.allowNegative){
36696             allowed += "-";
36697         }
36698         
36699         if(this.thousandsDelimiter) {
36700             allowed += ",";
36701         }
36702         
36703         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36704         
36705         var keyPress = function(e){
36706             
36707             var k = e.getKey();
36708             
36709             var c = e.getCharCode();
36710             
36711             if(
36712                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36713                     allowed.indexOf(String.fromCharCode(c)) === -1
36714             ){
36715                 e.stopEvent();
36716                 return;
36717             }
36718             
36719             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36720                 return;
36721             }
36722             
36723             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36724                 e.stopEvent();
36725             }
36726         };
36727         
36728         this.el.on("keypress", keyPress, this);
36729     },
36730     
36731     validateValue : function(value)
36732     {
36733         
36734         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36735             return false;
36736         }
36737         
36738         var num = this.parseValue(value);
36739         
36740         if(isNaN(num)){
36741             this.markInvalid(String.format(this.nanText, value));
36742             return false;
36743         }
36744         
36745         if(num < this.minValue){
36746             this.markInvalid(String.format(this.minText, this.minValue));
36747             return false;
36748         }
36749         
36750         if(num > this.maxValue){
36751             this.markInvalid(String.format(this.maxText, this.maxValue));
36752             return false;
36753         }
36754         
36755         return true;
36756     },
36757
36758     getValue : function()
36759     {
36760         var v = this.hiddenEl().getValue();
36761         
36762         return this.fixPrecision(this.parseValue(v));
36763     },
36764
36765     parseValue : function(value)
36766     {
36767         if(this.thousandsDelimiter) {
36768             value += "";
36769             r = new RegExp(",", "g");
36770             value = value.replace(r, "");
36771         }
36772         
36773         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36774         return isNaN(value) ? '' : value;
36775     },
36776
36777     fixPrecision : function(value)
36778     {
36779         if(this.thousandsDelimiter) {
36780             value += "";
36781             r = new RegExp(",", "g");
36782             value = value.replace(r, "");
36783         }
36784         
36785         var nan = isNaN(value);
36786         
36787         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36788             return nan ? '' : value;
36789         }
36790         return parseFloat(value).toFixed(this.decimalPrecision);
36791     },
36792
36793     setValue : function(v)
36794     {
36795         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36796         
36797         this.value = v;
36798         
36799         if(this.rendered){
36800             
36801             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36802             
36803             this.inputEl().dom.value = (v == '') ? '' :
36804                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36805             
36806             if(!this.allowZero && v === '0') {
36807                 this.hiddenEl().dom.value = '';
36808                 this.inputEl().dom.value = '';
36809             }
36810             
36811             this.validate();
36812         }
36813     },
36814
36815     decimalPrecisionFcn : function(v)
36816     {
36817         return Math.floor(v);
36818     },
36819
36820     beforeBlur : function()
36821     {
36822         var v = this.parseValue(this.getRawValue());
36823         
36824         if(v || v === 0 || v === ''){
36825             this.setValue(v);
36826         }
36827     },
36828     
36829     hiddenEl : function()
36830     {
36831         return this.el.select('input.hidden-number-input',true).first();
36832     }
36833     
36834 });
36835
36836  
36837
36838 /*
36839 * Licence: LGPL
36840 */
36841
36842 /**
36843  * @class Roo.bootstrap.DocumentSlider
36844  * @extends Roo.bootstrap.Component
36845  * Bootstrap DocumentSlider class
36846  * 
36847  * @constructor
36848  * Create a new DocumentViewer
36849  * @param {Object} config The config object
36850  */
36851
36852 Roo.bootstrap.DocumentSlider = function(config){
36853     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36854     
36855     this.files = [];
36856     
36857     this.addEvents({
36858         /**
36859          * @event initial
36860          * Fire after initEvent
36861          * @param {Roo.bootstrap.DocumentSlider} this
36862          */
36863         "initial" : true,
36864         /**
36865          * @event update
36866          * Fire after update
36867          * @param {Roo.bootstrap.DocumentSlider} this
36868          */
36869         "update" : true,
36870         /**
36871          * @event click
36872          * Fire after click
36873          * @param {Roo.bootstrap.DocumentSlider} this
36874          */
36875         "click" : true
36876     });
36877 };
36878
36879 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36880     
36881     files : false,
36882     
36883     indicator : 0,
36884     
36885     getAutoCreate : function()
36886     {
36887         var cfg = {
36888             tag : 'div',
36889             cls : 'roo-document-slider',
36890             cn : [
36891                 {
36892                     tag : 'div',
36893                     cls : 'roo-document-slider-header',
36894                     cn : [
36895                         {
36896                             tag : 'div',
36897                             cls : 'roo-document-slider-header-title'
36898                         }
36899                     ]
36900                 },
36901                 {
36902                     tag : 'div',
36903                     cls : 'roo-document-slider-body',
36904                     cn : [
36905                         {
36906                             tag : 'div',
36907                             cls : 'roo-document-slider-prev',
36908                             cn : [
36909                                 {
36910                                     tag : 'i',
36911                                     cls : 'fa fa-chevron-left'
36912                                 }
36913                             ]
36914                         },
36915                         {
36916                             tag : 'div',
36917                             cls : 'roo-document-slider-thumb',
36918                             cn : [
36919                                 {
36920                                     tag : 'img',
36921                                     cls : 'roo-document-slider-image'
36922                                 }
36923                             ]
36924                         },
36925                         {
36926                             tag : 'div',
36927                             cls : 'roo-document-slider-next',
36928                             cn : [
36929                                 {
36930                                     tag : 'i',
36931                                     cls : 'fa fa-chevron-right'
36932                                 }
36933                             ]
36934                         }
36935                     ]
36936                 }
36937             ]
36938         };
36939         
36940         return cfg;
36941     },
36942     
36943     initEvents : function()
36944     {
36945         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36946         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36947         
36948         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36949         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36950         
36951         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36952         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36953         
36954         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36955         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36956         
36957         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36958         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36959         
36960         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36961         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36962         
36963         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36964         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36965         
36966         this.thumbEl.on('click', this.onClick, this);
36967         
36968         this.prevIndicator.on('click', this.prev, this);
36969         
36970         this.nextIndicator.on('click', this.next, this);
36971         
36972     },
36973     
36974     initial : function()
36975     {
36976         if(this.files.length){
36977             this.indicator = 1;
36978             this.update()
36979         }
36980         
36981         this.fireEvent('initial', this);
36982     },
36983     
36984     update : function()
36985     {
36986         this.imageEl.attr('src', this.files[this.indicator - 1]);
36987         
36988         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36989         
36990         this.prevIndicator.show();
36991         
36992         if(this.indicator == 1){
36993             this.prevIndicator.hide();
36994         }
36995         
36996         this.nextIndicator.show();
36997         
36998         if(this.indicator == this.files.length){
36999             this.nextIndicator.hide();
37000         }
37001         
37002         this.thumbEl.scrollTo('top');
37003         
37004         this.fireEvent('update', this);
37005     },
37006     
37007     onClick : function(e)
37008     {
37009         e.preventDefault();
37010         
37011         this.fireEvent('click', this);
37012     },
37013     
37014     prev : function(e)
37015     {
37016         e.preventDefault();
37017         
37018         this.indicator = Math.max(1, this.indicator - 1);
37019         
37020         this.update();
37021     },
37022     
37023     next : function(e)
37024     {
37025         e.preventDefault();
37026         
37027         this.indicator = Math.min(this.files.length, this.indicator + 1);
37028         
37029         this.update();
37030     }
37031 });
37032 /*
37033  * - LGPL
37034  *
37035  * RadioSet
37036  *
37037  *
37038  */
37039
37040 /**
37041  * @class Roo.bootstrap.form.RadioSet
37042  * @extends Roo.bootstrap.form.Input
37043  * @children Roo.bootstrap.form.Radio
37044  * Bootstrap RadioSet class
37045  * @cfg {String} indicatorpos (left|right) default left
37046  * @cfg {Boolean} inline (true|false) inline the element (default true)
37047  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37048  * @constructor
37049  * Create a new RadioSet
37050  * @param {Object} config The config object
37051  */
37052
37053 Roo.bootstrap.form.RadioSet = function(config){
37054     
37055     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37056     
37057     this.radioes = [];
37058     
37059     Roo.bootstrap.form.RadioSet.register(this);
37060     
37061     this.addEvents({
37062         /**
37063         * @event check
37064         * Fires when the element is checked or unchecked.
37065         * @param {Roo.bootstrap.form.RadioSet} this This radio
37066         * @param {Roo.bootstrap.form.Radio} item The checked item
37067         */
37068        check : true,
37069        /**
37070         * @event click
37071         * Fires when the element is click.
37072         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37073         * @param {Roo.bootstrap.form.Radio} item The checked item
37074         * @param {Roo.EventObject} e The event object
37075         */
37076        click : true
37077     });
37078     
37079 };
37080
37081 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37082
37083     radioes : false,
37084     
37085     inline : true,
37086     
37087     weight : '',
37088     
37089     indicatorpos : 'left',
37090     
37091     getAutoCreate : function()
37092     {
37093         var label = {
37094             tag : 'label',
37095             cls : 'roo-radio-set-label',
37096             cn : [
37097                 {
37098                     tag : 'span',
37099                     html : this.fieldLabel
37100                 }
37101             ]
37102         };
37103         if (Roo.bootstrap.version == 3) {
37104             
37105             
37106             if(this.indicatorpos == 'left'){
37107                 label.cn.unshift({
37108                     tag : 'i',
37109                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37110                     tooltip : 'This field is required'
37111                 });
37112             } else {
37113                 label.cn.push({
37114                     tag : 'i',
37115                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37116                     tooltip : 'This field is required'
37117                 });
37118             }
37119         }
37120         var items = {
37121             tag : 'div',
37122             cls : 'roo-radio-set-items'
37123         };
37124         
37125         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37126         
37127         if (align === 'left' && this.fieldLabel.length) {
37128             
37129             items = {
37130                 cls : "roo-radio-set-right", 
37131                 cn: [
37132                     items
37133                 ]
37134             };
37135             
37136             if(this.labelWidth > 12){
37137                 label.style = "width: " + this.labelWidth + 'px';
37138             }
37139             
37140             if(this.labelWidth < 13 && this.labelmd == 0){
37141                 this.labelmd = this.labelWidth;
37142             }
37143             
37144             if(this.labellg > 0){
37145                 label.cls += ' col-lg-' + this.labellg;
37146                 items.cls += ' col-lg-' + (12 - this.labellg);
37147             }
37148             
37149             if(this.labelmd > 0){
37150                 label.cls += ' col-md-' + this.labelmd;
37151                 items.cls += ' col-md-' + (12 - this.labelmd);
37152             }
37153             
37154             if(this.labelsm > 0){
37155                 label.cls += ' col-sm-' + this.labelsm;
37156                 items.cls += ' col-sm-' + (12 - this.labelsm);
37157             }
37158             
37159             if(this.labelxs > 0){
37160                 label.cls += ' col-xs-' + this.labelxs;
37161                 items.cls += ' col-xs-' + (12 - this.labelxs);
37162             }
37163         }
37164         
37165         var cfg = {
37166             tag : 'div',
37167             cls : 'roo-radio-set',
37168             cn : [
37169                 {
37170                     tag : 'input',
37171                     cls : 'roo-radio-set-input',
37172                     type : 'hidden',
37173                     name : this.name,
37174                     value : this.value ? this.value :  ''
37175                 },
37176                 label,
37177                 items
37178             ]
37179         };
37180         
37181         if(this.weight.length){
37182             cfg.cls += ' roo-radio-' + this.weight;
37183         }
37184         
37185         if(this.inline) {
37186             cfg.cls += ' roo-radio-set-inline';
37187         }
37188         
37189         var settings=this;
37190         ['xs','sm','md','lg'].map(function(size){
37191             if (settings[size]) {
37192                 cfg.cls += ' col-' + size + '-' + settings[size];
37193             }
37194         });
37195         
37196         return cfg;
37197         
37198     },
37199
37200     initEvents : function()
37201     {
37202         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37203         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37204         
37205         if(!this.fieldLabel.length){
37206             this.labelEl.hide();
37207         }
37208         
37209         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37210         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37211         
37212         this.indicator = this.indicatorEl();
37213         
37214         if(this.indicator){
37215             this.indicator.addClass('invisible');
37216         }
37217         
37218         this.originalValue = this.getValue();
37219         
37220     },
37221     
37222     inputEl: function ()
37223     {
37224         return this.el.select('.roo-radio-set-input', true).first();
37225     },
37226     
37227     getChildContainer : function()
37228     {
37229         return this.itemsEl;
37230     },
37231     
37232     register : function(item)
37233     {
37234         this.radioes.push(item);
37235         
37236     },
37237     
37238     validate : function()
37239     {   
37240         if(this.getVisibilityEl().hasClass('hidden')){
37241             return true;
37242         }
37243         
37244         var valid = false;
37245         
37246         Roo.each(this.radioes, function(i){
37247             if(!i.checked){
37248                 return;
37249             }
37250             
37251             valid = true;
37252             return false;
37253         });
37254         
37255         if(this.allowBlank) {
37256             return true;
37257         }
37258         
37259         if(this.disabled || valid){
37260             this.markValid();
37261             return true;
37262         }
37263         
37264         this.markInvalid();
37265         return false;
37266         
37267     },
37268     
37269     markValid : function()
37270     {
37271         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37272             this.indicatorEl().removeClass('visible');
37273             this.indicatorEl().addClass('invisible');
37274         }
37275         
37276         
37277         if (Roo.bootstrap.version == 3) {
37278             this.el.removeClass([this.invalidClass, this.validClass]);
37279             this.el.addClass(this.validClass);
37280         } else {
37281             this.el.removeClass(['is-invalid','is-valid']);
37282             this.el.addClass(['is-valid']);
37283         }
37284         this.fireEvent('valid', this);
37285     },
37286     
37287     markInvalid : function(msg)
37288     {
37289         if(this.allowBlank || this.disabled){
37290             return;
37291         }
37292         
37293         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37294             this.indicatorEl().removeClass('invisible');
37295             this.indicatorEl().addClass('visible');
37296         }
37297         if (Roo.bootstrap.version == 3) {
37298             this.el.removeClass([this.invalidClass, this.validClass]);
37299             this.el.addClass(this.invalidClass);
37300         } else {
37301             this.el.removeClass(['is-invalid','is-valid']);
37302             this.el.addClass(['is-invalid']);
37303         }
37304         
37305         this.fireEvent('invalid', this, msg);
37306         
37307     },
37308     
37309     setValue : function(v, suppressEvent)
37310     {   
37311         if(this.value === v){
37312             return;
37313         }
37314         
37315         this.value = v;
37316         
37317         if(this.rendered){
37318             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37319         }
37320         
37321         Roo.each(this.radioes, function(i){
37322             i.checked = false;
37323             i.el.removeClass('checked');
37324         });
37325         
37326         Roo.each(this.radioes, function(i){
37327             
37328             if(i.value === v || i.value.toString() === v.toString()){
37329                 i.checked = true;
37330                 i.el.addClass('checked');
37331                 
37332                 if(suppressEvent !== true){
37333                     this.fireEvent('check', this, i);
37334                 }
37335                 
37336                 return false;
37337             }
37338             
37339         }, this);
37340         
37341         this.validate();
37342     },
37343     
37344     clearInvalid : function(){
37345         
37346         if(!this.el || this.preventMark){
37347             return;
37348         }
37349         
37350         this.el.removeClass([this.invalidClass]);
37351         
37352         this.fireEvent('valid', this);
37353     }
37354     
37355 });
37356
37357 Roo.apply(Roo.bootstrap.form.RadioSet, {
37358     
37359     groups: {},
37360     
37361     register : function(set)
37362     {
37363         this.groups[set.name] = set;
37364     },
37365     
37366     get: function(name) 
37367     {
37368         if (typeof(this.groups[name]) == 'undefined') {
37369             return false;
37370         }
37371         
37372         return this.groups[name] ;
37373     }
37374     
37375 });
37376 /*
37377  * Based on:
37378  * Ext JS Library 1.1.1
37379  * Copyright(c) 2006-2007, Ext JS, LLC.
37380  *
37381  * Originally Released Under LGPL - original licence link has changed is not relivant.
37382  *
37383  * Fork - LGPL
37384  * <script type="text/javascript">
37385  */
37386
37387
37388 /**
37389  * @class Roo.bootstrap.SplitBar
37390  * @extends Roo.util.Observable
37391  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37392  * <br><br>
37393  * Usage:
37394  * <pre><code>
37395 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37396                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37397 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37398 split.minSize = 100;
37399 split.maxSize = 600;
37400 split.animate = true;
37401 split.on('moved', splitterMoved);
37402 </code></pre>
37403  * @constructor
37404  * Create a new SplitBar
37405  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37406  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37407  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37408  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37409                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37410                         position of the SplitBar).
37411  */
37412 Roo.bootstrap.SplitBar = function(cfg){
37413     
37414     /** @private */
37415     
37416     //{
37417     //  dragElement : elm
37418     //  resizingElement: el,
37419         // optional..
37420     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37421     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37422         // existingProxy ???
37423     //}
37424     
37425     this.el = Roo.get(cfg.dragElement, true);
37426     this.el.dom.unselectable = "on";
37427     /** @private */
37428     this.resizingEl = Roo.get(cfg.resizingElement, true);
37429
37430     /**
37431      * @private
37432      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37433      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37434      * @type Number
37435      */
37436     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37437     
37438     /**
37439      * The minimum size of the resizing element. (Defaults to 0)
37440      * @type Number
37441      */
37442     this.minSize = 0;
37443     
37444     /**
37445      * The maximum size of the resizing element. (Defaults to 2000)
37446      * @type Number
37447      */
37448     this.maxSize = 2000;
37449     
37450     /**
37451      * Whether to animate the transition to the new size
37452      * @type Boolean
37453      */
37454     this.animate = false;
37455     
37456     /**
37457      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37458      * @type Boolean
37459      */
37460     this.useShim = false;
37461     
37462     /** @private */
37463     this.shim = null;
37464     
37465     if(!cfg.existingProxy){
37466         /** @private */
37467         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37468     }else{
37469         this.proxy = Roo.get(cfg.existingProxy).dom;
37470     }
37471     /** @private */
37472     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37473     
37474     /** @private */
37475     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37476     
37477     /** @private */
37478     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37479     
37480     /** @private */
37481     this.dragSpecs = {};
37482     
37483     /**
37484      * @private The adapter to use to positon and resize elements
37485      */
37486     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37487     this.adapter.init(this);
37488     
37489     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37490         /** @private */
37491         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37492         this.el.addClass("roo-splitbar-h");
37493     }else{
37494         /** @private */
37495         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37496         this.el.addClass("roo-splitbar-v");
37497     }
37498     
37499     this.addEvents({
37500         /**
37501          * @event resize
37502          * Fires when the splitter is moved (alias for {@link #event-moved})
37503          * @param {Roo.bootstrap.SplitBar} this
37504          * @param {Number} newSize the new width or height
37505          */
37506         "resize" : true,
37507         /**
37508          * @event moved
37509          * Fires when the splitter is moved
37510          * @param {Roo.bootstrap.SplitBar} this
37511          * @param {Number} newSize the new width or height
37512          */
37513         "moved" : true,
37514         /**
37515          * @event beforeresize
37516          * Fires before the splitter is dragged
37517          * @param {Roo.bootstrap.SplitBar} this
37518          */
37519         "beforeresize" : true,
37520
37521         "beforeapply" : true
37522     });
37523
37524     Roo.util.Observable.call(this);
37525 };
37526
37527 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37528     onStartProxyDrag : function(x, y){
37529         this.fireEvent("beforeresize", this);
37530         if(!this.overlay){
37531             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37532             o.unselectable();
37533             o.enableDisplayMode("block");
37534             // all splitbars share the same overlay
37535             Roo.bootstrap.SplitBar.prototype.overlay = o;
37536         }
37537         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37538         this.overlay.show();
37539         Roo.get(this.proxy).setDisplayed("block");
37540         var size = this.adapter.getElementSize(this);
37541         this.activeMinSize = this.getMinimumSize();;
37542         this.activeMaxSize = this.getMaximumSize();;
37543         var c1 = size - this.activeMinSize;
37544         var c2 = Math.max(this.activeMaxSize - size, 0);
37545         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37546             this.dd.resetConstraints();
37547             this.dd.setXConstraint(
37548                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37549                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37550             );
37551             this.dd.setYConstraint(0, 0);
37552         }else{
37553             this.dd.resetConstraints();
37554             this.dd.setXConstraint(0, 0);
37555             this.dd.setYConstraint(
37556                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37557                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37558             );
37559          }
37560         this.dragSpecs.startSize = size;
37561         this.dragSpecs.startPoint = [x, y];
37562         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37563     },
37564     
37565     /** 
37566      * @private Called after the drag operation by the DDProxy
37567      */
37568     onEndProxyDrag : function(e){
37569         Roo.get(this.proxy).setDisplayed(false);
37570         var endPoint = Roo.lib.Event.getXY(e);
37571         if(this.overlay){
37572             this.overlay.hide();
37573         }
37574         var newSize;
37575         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37576             newSize = this.dragSpecs.startSize + 
37577                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37578                     endPoint[0] - this.dragSpecs.startPoint[0] :
37579                     this.dragSpecs.startPoint[0] - endPoint[0]
37580                 );
37581         }else{
37582             newSize = this.dragSpecs.startSize + 
37583                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37584                     endPoint[1] - this.dragSpecs.startPoint[1] :
37585                     this.dragSpecs.startPoint[1] - endPoint[1]
37586                 );
37587         }
37588         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37589         if(newSize != this.dragSpecs.startSize){
37590             if(this.fireEvent('beforeapply', this, newSize) !== false){
37591                 this.adapter.setElementSize(this, newSize);
37592                 this.fireEvent("moved", this, newSize);
37593                 this.fireEvent("resize", this, newSize);
37594             }
37595         }
37596     },
37597     
37598     /**
37599      * Get the adapter this SplitBar uses
37600      * @return The adapter object
37601      */
37602     getAdapter : function(){
37603         return this.adapter;
37604     },
37605     
37606     /**
37607      * Set the adapter this SplitBar uses
37608      * @param {Object} adapter A SplitBar adapter object
37609      */
37610     setAdapter : function(adapter){
37611         this.adapter = adapter;
37612         this.adapter.init(this);
37613     },
37614     
37615     /**
37616      * Gets the minimum size for the resizing element
37617      * @return {Number} The minimum size
37618      */
37619     getMinimumSize : function(){
37620         return this.minSize;
37621     },
37622     
37623     /**
37624      * Sets the minimum size for the resizing element
37625      * @param {Number} minSize The minimum size
37626      */
37627     setMinimumSize : function(minSize){
37628         this.minSize = minSize;
37629     },
37630     
37631     /**
37632      * Gets the maximum size for the resizing element
37633      * @return {Number} The maximum size
37634      */
37635     getMaximumSize : function(){
37636         return this.maxSize;
37637     },
37638     
37639     /**
37640      * Sets the maximum size for the resizing element
37641      * @param {Number} maxSize The maximum size
37642      */
37643     setMaximumSize : function(maxSize){
37644         this.maxSize = maxSize;
37645     },
37646     
37647     /**
37648      * Sets the initialize size for the resizing element
37649      * @param {Number} size The initial size
37650      */
37651     setCurrentSize : function(size){
37652         var oldAnimate = this.animate;
37653         this.animate = false;
37654         this.adapter.setElementSize(this, size);
37655         this.animate = oldAnimate;
37656     },
37657     
37658     /**
37659      * Destroy this splitbar. 
37660      * @param {Boolean} removeEl True to remove the element
37661      */
37662     destroy : function(removeEl){
37663         if(this.shim){
37664             this.shim.remove();
37665         }
37666         this.dd.unreg();
37667         this.proxy.parentNode.removeChild(this.proxy);
37668         if(removeEl){
37669             this.el.remove();
37670         }
37671     }
37672 });
37673
37674 /**
37675  * @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.
37676  */
37677 Roo.bootstrap.SplitBar.createProxy = function(dir){
37678     var proxy = new Roo.Element(document.createElement("div"));
37679     proxy.unselectable();
37680     var cls = 'roo-splitbar-proxy';
37681     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37682     document.body.appendChild(proxy.dom);
37683     return proxy.dom;
37684 };
37685
37686 /** 
37687  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37688  * Default Adapter. It assumes the splitter and resizing element are not positioned
37689  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37690  */
37691 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37692 };
37693
37694 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37695     // do nothing for now
37696     init : function(s){
37697     
37698     },
37699     /**
37700      * Called before drag operations to get the current size of the resizing element. 
37701      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37702      */
37703      getElementSize : function(s){
37704         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37705             return s.resizingEl.getWidth();
37706         }else{
37707             return s.resizingEl.getHeight();
37708         }
37709     },
37710     
37711     /**
37712      * Called after drag operations to set the size of the resizing element.
37713      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37714      * @param {Number} newSize The new size to set
37715      * @param {Function} onComplete A function to be invoked when resizing is complete
37716      */
37717     setElementSize : function(s, newSize, onComplete){
37718         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37719             if(!s.animate){
37720                 s.resizingEl.setWidth(newSize);
37721                 if(onComplete){
37722                     onComplete(s, newSize);
37723                 }
37724             }else{
37725                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37726             }
37727         }else{
37728             
37729             if(!s.animate){
37730                 s.resizingEl.setHeight(newSize);
37731                 if(onComplete){
37732                     onComplete(s, newSize);
37733                 }
37734             }else{
37735                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37736             }
37737         }
37738     }
37739 };
37740
37741 /** 
37742  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37743  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37744  * Adapter that  moves the splitter element to align with the resized sizing element. 
37745  * Used with an absolute positioned SplitBar.
37746  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37747  * document.body, make sure you assign an id to the body element.
37748  */
37749 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37750     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37751     this.container = Roo.get(container);
37752 };
37753
37754 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37755     init : function(s){
37756         this.basic.init(s);
37757     },
37758     
37759     getElementSize : function(s){
37760         return this.basic.getElementSize(s);
37761     },
37762     
37763     setElementSize : function(s, newSize, onComplete){
37764         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37765     },
37766     
37767     moveSplitter : function(s){
37768         var yes = Roo.bootstrap.SplitBar;
37769         switch(s.placement){
37770             case yes.LEFT:
37771                 s.el.setX(s.resizingEl.getRight());
37772                 break;
37773             case yes.RIGHT:
37774                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37775                 break;
37776             case yes.TOP:
37777                 s.el.setY(s.resizingEl.getBottom());
37778                 break;
37779             case yes.BOTTOM:
37780                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37781                 break;
37782         }
37783     }
37784 };
37785
37786 /**
37787  * Orientation constant - Create a vertical SplitBar
37788  * @static
37789  * @type Number
37790  */
37791 Roo.bootstrap.SplitBar.VERTICAL = 1;
37792
37793 /**
37794  * Orientation constant - Create a horizontal SplitBar
37795  * @static
37796  * @type Number
37797  */
37798 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37799
37800 /**
37801  * Placement constant - The resizing element is to the left of the splitter element
37802  * @static
37803  * @type Number
37804  */
37805 Roo.bootstrap.SplitBar.LEFT = 1;
37806
37807 /**
37808  * Placement constant - The resizing element is to the right of the splitter element
37809  * @static
37810  * @type Number
37811  */
37812 Roo.bootstrap.SplitBar.RIGHT = 2;
37813
37814 /**
37815  * Placement constant - The resizing element is positioned above the splitter element
37816  * @static
37817  * @type Number
37818  */
37819 Roo.bootstrap.SplitBar.TOP = 3;
37820
37821 /**
37822  * Placement constant - The resizing element is positioned under splitter element
37823  * @static
37824  * @type Number
37825  */
37826 Roo.bootstrap.SplitBar.BOTTOM = 4;
37827 /*
37828  * Based on:
37829  * Ext JS Library 1.1.1
37830  * Copyright(c) 2006-2007, Ext JS, LLC.
37831  *
37832  * Originally Released Under LGPL - original licence link has changed is not relivant.
37833  *
37834  * Fork - LGPL
37835  * <script type="text/javascript">
37836  */
37837
37838 /**
37839  * @class Roo.bootstrap.layout.Manager
37840  * @extends Roo.bootstrap.Component
37841  * @abstract
37842  * Base class for layout managers.
37843  */
37844 Roo.bootstrap.layout.Manager = function(config)
37845 {
37846     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37847
37848
37849
37850
37851
37852     /** false to disable window resize monitoring @type Boolean */
37853     this.monitorWindowResize = true;
37854     this.regions = {};
37855     this.addEvents({
37856         /**
37857          * @event layout
37858          * Fires when a layout is performed.
37859          * @param {Roo.LayoutManager} this
37860          */
37861         "layout" : true,
37862         /**
37863          * @event regionresized
37864          * Fires when the user resizes a region.
37865          * @param {Roo.LayoutRegion} region The resized region
37866          * @param {Number} newSize The new size (width for east/west, height for north/south)
37867          */
37868         "regionresized" : true,
37869         /**
37870          * @event regioncollapsed
37871          * Fires when a region is collapsed.
37872          * @param {Roo.LayoutRegion} region The collapsed region
37873          */
37874         "regioncollapsed" : true,
37875         /**
37876          * @event regionexpanded
37877          * Fires when a region is expanded.
37878          * @param {Roo.LayoutRegion} region The expanded region
37879          */
37880         "regionexpanded" : true
37881     });
37882     this.updating = false;
37883
37884     if (config.el) {
37885         this.el = Roo.get(config.el);
37886         this.initEvents();
37887     }
37888
37889 };
37890
37891 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37892
37893
37894     regions : null,
37895
37896     monitorWindowResize : true,
37897
37898
37899     updating : false,
37900
37901
37902     onRender : function(ct, position)
37903     {
37904         if(!this.el){
37905             this.el = Roo.get(ct);
37906             this.initEvents();
37907         }
37908         //this.fireEvent('render',this);
37909     },
37910
37911
37912     initEvents: function()
37913     {
37914
37915
37916         // ie scrollbar fix
37917         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37918             document.body.scroll = "no";
37919         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37920             this.el.position('relative');
37921         }
37922         this.id = this.el.id;
37923         this.el.addClass("roo-layout-container");
37924         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37925         if(this.el.dom != document.body ) {
37926             this.el.on('resize', this.layout,this);
37927             this.el.on('show', this.layout,this);
37928         }
37929
37930     },
37931
37932     /**
37933      * Returns true if this layout is currently being updated
37934      * @return {Boolean}
37935      */
37936     isUpdating : function(){
37937         return this.updating;
37938     },
37939
37940     /**
37941      * Suspend the LayoutManager from doing auto-layouts while
37942      * making multiple add or remove calls
37943      */
37944     beginUpdate : function(){
37945         this.updating = true;
37946     },
37947
37948     /**
37949      * Restore auto-layouts and optionally disable the manager from performing a layout
37950      * @param {Boolean} noLayout true to disable a layout update
37951      */
37952     endUpdate : function(noLayout){
37953         this.updating = false;
37954         if(!noLayout){
37955             this.layout();
37956         }
37957     },
37958
37959     layout: function(){
37960         // abstract...
37961     },
37962
37963     onRegionResized : function(region, newSize){
37964         this.fireEvent("regionresized", region, newSize);
37965         this.layout();
37966     },
37967
37968     onRegionCollapsed : function(region){
37969         this.fireEvent("regioncollapsed", region);
37970     },
37971
37972     onRegionExpanded : function(region){
37973         this.fireEvent("regionexpanded", region);
37974     },
37975
37976     /**
37977      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37978      * performs box-model adjustments.
37979      * @return {Object} The size as an object {width: (the width), height: (the height)}
37980      */
37981     getViewSize : function()
37982     {
37983         var size;
37984         if(this.el.dom != document.body){
37985             size = this.el.getSize();
37986         }else{
37987             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37988         }
37989         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37990         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37991         return size;
37992     },
37993
37994     /**
37995      * Returns the Element this layout is bound to.
37996      * @return {Roo.Element}
37997      */
37998     getEl : function(){
37999         return this.el;
38000     },
38001
38002     /**
38003      * Returns the specified region.
38004      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38005      * @return {Roo.LayoutRegion}
38006      */
38007     getRegion : function(target){
38008         return this.regions[target.toLowerCase()];
38009     },
38010
38011     onWindowResize : function(){
38012         if(this.monitorWindowResize){
38013             this.layout();
38014         }
38015     }
38016 });
38017 /*
38018  * Based on:
38019  * Ext JS Library 1.1.1
38020  * Copyright(c) 2006-2007, Ext JS, LLC.
38021  *
38022  * Originally Released Under LGPL - original licence link has changed is not relivant.
38023  *
38024  * Fork - LGPL
38025  * <script type="text/javascript">
38026  */
38027 /**
38028  * @class Roo.bootstrap.layout.Border
38029  * @extends Roo.bootstrap.layout.Manager
38030  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38031  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38032  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38033  * please see: examples/bootstrap/nested.html<br><br>
38034  
38035 <b>The container the layout is rendered into can be either the body element or any other element.
38036 If it is not the body element, the container needs to either be an absolute positioned element,
38037 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38038 the container size if it is not the body element.</b>
38039
38040 * @constructor
38041 * Create a new Border
38042 * @param {Object} config Configuration options
38043  */
38044 Roo.bootstrap.layout.Border = function(config){
38045     config = config || {};
38046     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38047     
38048     
38049     
38050     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38051         if(config[region]){
38052             config[region].region = region;
38053             this.addRegion(config[region]);
38054         }
38055     },this);
38056     
38057 };
38058
38059 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38060
38061 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38062     
38063         /**
38064          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38065          */
38066         /**
38067          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38068          */
38069         /**
38070          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38071          */
38072         /**
38073          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38074          */
38075         /**
38076          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38077          */
38078         
38079         
38080         
38081         
38082     parent : false, // this might point to a 'nest' or a ???
38083     
38084     /**
38085      * Creates and adds a new region if it doesn't already exist.
38086      * @param {String} target The target region key (north, south, east, west or center).
38087      * @param {Object} config The regions config object
38088      * @return {BorderLayoutRegion} The new region
38089      */
38090     addRegion : function(config)
38091     {
38092         if(!this.regions[config.region]){
38093             var r = this.factory(config);
38094             this.bindRegion(r);
38095         }
38096         return this.regions[config.region];
38097     },
38098
38099     // private (kinda)
38100     bindRegion : function(r){
38101         this.regions[r.config.region] = r;
38102         
38103         r.on("visibilitychange",    this.layout, this);
38104         r.on("paneladded",          this.layout, this);
38105         r.on("panelremoved",        this.layout, this);
38106         r.on("invalidated",         this.layout, this);
38107         r.on("resized",             this.onRegionResized, this);
38108         r.on("collapsed",           this.onRegionCollapsed, this);
38109         r.on("expanded",            this.onRegionExpanded, this);
38110     },
38111
38112     /**
38113      * Performs a layout update.
38114      */
38115     layout : function()
38116     {
38117         if(this.updating) {
38118             return;
38119         }
38120         
38121         // render all the rebions if they have not been done alreayd?
38122         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38123             if(this.regions[region] && !this.regions[region].bodyEl){
38124                 this.regions[region].onRender(this.el)
38125             }
38126         },this);
38127         
38128         var size = this.getViewSize();
38129         var w = size.width;
38130         var h = size.height;
38131         var centerW = w;
38132         var centerH = h;
38133         var centerY = 0;
38134         var centerX = 0;
38135         //var x = 0, y = 0;
38136
38137         var rs = this.regions;
38138         var north = rs["north"];
38139         var south = rs["south"]; 
38140         var west = rs["west"];
38141         var east = rs["east"];
38142         var center = rs["center"];
38143         //if(this.hideOnLayout){ // not supported anymore
38144             //c.el.setStyle("display", "none");
38145         //}
38146         if(north && north.isVisible()){
38147             var b = north.getBox();
38148             var m = north.getMargins();
38149             b.width = w - (m.left+m.right);
38150             b.x = m.left;
38151             b.y = m.top;
38152             centerY = b.height + b.y + m.bottom;
38153             centerH -= centerY;
38154             north.updateBox(this.safeBox(b));
38155         }
38156         if(south && south.isVisible()){
38157             var b = south.getBox();
38158             var m = south.getMargins();
38159             b.width = w - (m.left+m.right);
38160             b.x = m.left;
38161             var totalHeight = (b.height + m.top + m.bottom);
38162             b.y = h - totalHeight + m.top;
38163             centerH -= totalHeight;
38164             south.updateBox(this.safeBox(b));
38165         }
38166         if(west && west.isVisible()){
38167             var b = west.getBox();
38168             var m = west.getMargins();
38169             b.height = centerH - (m.top+m.bottom);
38170             b.x = m.left;
38171             b.y = centerY + m.top;
38172             var totalWidth = (b.width + m.left + m.right);
38173             centerX += totalWidth;
38174             centerW -= totalWidth;
38175             west.updateBox(this.safeBox(b));
38176         }
38177         if(east && east.isVisible()){
38178             var b = east.getBox();
38179             var m = east.getMargins();
38180             b.height = centerH - (m.top+m.bottom);
38181             var totalWidth = (b.width + m.left + m.right);
38182             b.x = w - totalWidth + m.left;
38183             b.y = centerY + m.top;
38184             centerW -= totalWidth;
38185             east.updateBox(this.safeBox(b));
38186         }
38187         if(center){
38188             var m = center.getMargins();
38189             var centerBox = {
38190                 x: centerX + m.left,
38191                 y: centerY + m.top,
38192                 width: centerW - (m.left+m.right),
38193                 height: centerH - (m.top+m.bottom)
38194             };
38195             //if(this.hideOnLayout){
38196                 //center.el.setStyle("display", "block");
38197             //}
38198             center.updateBox(this.safeBox(centerBox));
38199         }
38200         this.el.repaint();
38201         this.fireEvent("layout", this);
38202     },
38203
38204     // private
38205     safeBox : function(box){
38206         box.width = Math.max(0, box.width);
38207         box.height = Math.max(0, box.height);
38208         return box;
38209     },
38210
38211     /**
38212      * Adds a ContentPanel (or subclass) to this layout.
38213      * @param {String} target The target region key (north, south, east, west or center).
38214      * @param {Roo.ContentPanel} panel The panel to add
38215      * @return {Roo.ContentPanel} The added panel
38216      */
38217     add : function(target, panel){
38218          
38219         target = target.toLowerCase();
38220         return this.regions[target].add(panel);
38221     },
38222
38223     /**
38224      * Remove a ContentPanel (or subclass) to this layout.
38225      * @param {String} target The target region key (north, south, east, west or center).
38226      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38227      * @return {Roo.ContentPanel} The removed panel
38228      */
38229     remove : function(target, panel){
38230         target = target.toLowerCase();
38231         return this.regions[target].remove(panel);
38232     },
38233
38234     /**
38235      * Searches all regions for a panel with the specified id
38236      * @param {String} panelId
38237      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38238      */
38239     findPanel : function(panelId){
38240         var rs = this.regions;
38241         for(var target in rs){
38242             if(typeof rs[target] != "function"){
38243                 var p = rs[target].getPanel(panelId);
38244                 if(p){
38245                     return p;
38246                 }
38247             }
38248         }
38249         return null;
38250     },
38251
38252     /**
38253      * Searches all regions for a panel with the specified id and activates (shows) it.
38254      * @param {String/ContentPanel} panelId The panels id or the panel itself
38255      * @return {Roo.ContentPanel} The shown panel or null
38256      */
38257     showPanel : function(panelId) {
38258       var rs = this.regions;
38259       for(var target in rs){
38260          var r = rs[target];
38261          if(typeof r != "function"){
38262             if(r.hasPanel(panelId)){
38263                return r.showPanel(panelId);
38264             }
38265          }
38266       }
38267       return null;
38268    },
38269
38270    /**
38271      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38272      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38273      */
38274    /*
38275     restoreState : function(provider){
38276         if(!provider){
38277             provider = Roo.state.Manager;
38278         }
38279         var sm = new Roo.LayoutStateManager();
38280         sm.init(this, provider);
38281     },
38282 */
38283  
38284  
38285     /**
38286      * Adds a xtype elements to the layout.
38287      * <pre><code>
38288
38289 layout.addxtype({
38290        xtype : 'ContentPanel',
38291        region: 'west',
38292        items: [ .... ]
38293    }
38294 );
38295
38296 layout.addxtype({
38297         xtype : 'NestedLayoutPanel',
38298         region: 'west',
38299         layout: {
38300            center: { },
38301            west: { }   
38302         },
38303         items : [ ... list of content panels or nested layout panels.. ]
38304    }
38305 );
38306 </code></pre>
38307      * @param {Object} cfg Xtype definition of item to add.
38308      */
38309     addxtype : function(cfg)
38310     {
38311         // basically accepts a pannel...
38312         // can accept a layout region..!?!?
38313         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38314         
38315         
38316         // theory?  children can only be panels??
38317         
38318         //if (!cfg.xtype.match(/Panel$/)) {
38319         //    return false;
38320         //}
38321         var ret = false;
38322         
38323         if (typeof(cfg.region) == 'undefined') {
38324             Roo.log("Failed to add Panel, region was not set");
38325             Roo.log(cfg);
38326             return false;
38327         }
38328         var region = cfg.region;
38329         delete cfg.region;
38330         
38331           
38332         var xitems = [];
38333         if (cfg.items) {
38334             xitems = cfg.items;
38335             delete cfg.items;
38336         }
38337         var nb = false;
38338         
38339         if ( region == 'center') {
38340             Roo.log("Center: " + cfg.title);
38341         }
38342         
38343         
38344         switch(cfg.xtype) 
38345         {
38346             case 'Content':  // ContentPanel (el, cfg)
38347             case 'Scroll':  // ContentPanel (el, cfg)
38348             case 'View': 
38349                 cfg.autoCreate = cfg.autoCreate || true;
38350                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38351                 //} else {
38352                 //    var el = this.el.createChild();
38353                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38354                 //}
38355                 
38356                 this.add(region, ret);
38357                 break;
38358             
38359             /*
38360             case 'TreePanel': // our new panel!
38361                 cfg.el = this.el.createChild();
38362                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38363                 this.add(region, ret);
38364                 break;
38365             */
38366             
38367             case 'Nest': 
38368                 // create a new Layout (which is  a Border Layout...
38369                 
38370                 var clayout = cfg.layout;
38371                 clayout.el  = this.el.createChild();
38372                 clayout.items   = clayout.items  || [];
38373                 
38374                 delete cfg.layout;
38375                 
38376                 // replace this exitems with the clayout ones..
38377                 xitems = clayout.items;
38378                  
38379                 // force background off if it's in center...
38380                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38381                     cfg.background = false;
38382                 }
38383                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38384                 
38385                 
38386                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38387                 //console.log('adding nested layout panel '  + cfg.toSource());
38388                 this.add(region, ret);
38389                 nb = {}; /// find first...
38390                 break;
38391             
38392             case 'Grid':
38393                 
38394                 // needs grid and region
38395                 
38396                 //var el = this.getRegion(region).el.createChild();
38397                 /*
38398                  *var el = this.el.createChild();
38399                 // create the grid first...
38400                 cfg.grid.container = el;
38401                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38402                 */
38403                 
38404                 if (region == 'center' && this.active ) {
38405                     cfg.background = false;
38406                 }
38407                 
38408                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38409                 
38410                 this.add(region, ret);
38411                 /*
38412                 if (cfg.background) {
38413                     // render grid on panel activation (if panel background)
38414                     ret.on('activate', function(gp) {
38415                         if (!gp.grid.rendered) {
38416                     //        gp.grid.render(el);
38417                         }
38418                     });
38419                 } else {
38420                   //  cfg.grid.render(el);
38421                 }
38422                 */
38423                 break;
38424            
38425            
38426             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38427                 // it was the old xcomponent building that caused this before.
38428                 // espeically if border is the top element in the tree.
38429                 ret = this;
38430                 break; 
38431                 
38432                     
38433                 
38434                 
38435                 
38436             default:
38437                 /*
38438                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38439                     
38440                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38441                     this.add(region, ret);
38442                 } else {
38443                 */
38444                     Roo.log(cfg);
38445                     throw "Can not add '" + cfg.xtype + "' to Border";
38446                     return null;
38447              
38448                                 
38449              
38450         }
38451         this.beginUpdate();
38452         // add children..
38453         var region = '';
38454         var abn = {};
38455         Roo.each(xitems, function(i)  {
38456             region = nb && i.region ? i.region : false;
38457             
38458             var add = ret.addxtype(i);
38459            
38460             if (region) {
38461                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38462                 if (!i.background) {
38463                     abn[region] = nb[region] ;
38464                 }
38465             }
38466             
38467         });
38468         this.endUpdate();
38469
38470         // make the last non-background panel active..
38471         //if (nb) { Roo.log(abn); }
38472         if (nb) {
38473             
38474             for(var r in abn) {
38475                 region = this.getRegion(r);
38476                 if (region) {
38477                     // tried using nb[r], but it does not work..
38478                      
38479                     region.showPanel(abn[r]);
38480                    
38481                 }
38482             }
38483         }
38484         return ret;
38485         
38486     },
38487     
38488     
38489 // private
38490     factory : function(cfg)
38491     {
38492         
38493         var validRegions = Roo.bootstrap.layout.Border.regions;
38494
38495         var target = cfg.region;
38496         cfg.mgr = this;
38497         
38498         var r = Roo.bootstrap.layout;
38499         Roo.log(target);
38500         switch(target){
38501             case "north":
38502                 return new r.North(cfg);
38503             case "south":
38504                 return new r.South(cfg);
38505             case "east":
38506                 return new r.East(cfg);
38507             case "west":
38508                 return new r.West(cfg);
38509             case "center":
38510                 return new r.Center(cfg);
38511         }
38512         throw 'Layout region "'+target+'" not supported.';
38513     }
38514     
38515     
38516 });
38517  /*
38518  * Based on:
38519  * Ext JS Library 1.1.1
38520  * Copyright(c) 2006-2007, Ext JS, LLC.
38521  *
38522  * Originally Released Under LGPL - original licence link has changed is not relivant.
38523  *
38524  * Fork - LGPL
38525  * <script type="text/javascript">
38526  */
38527  
38528 /**
38529  * @class Roo.bootstrap.layout.Basic
38530  * @extends Roo.util.Observable
38531  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38532  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38533  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38534  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38535  * @cfg {string}   region  the region that it inhabits..
38536  * @cfg {bool}   skipConfig skip config?
38537  * 
38538
38539  */
38540 Roo.bootstrap.layout.Basic = function(config){
38541     
38542     this.mgr = config.mgr;
38543     
38544     this.position = config.region;
38545     
38546     var skipConfig = config.skipConfig;
38547     
38548     this.events = {
38549         /**
38550          * @scope Roo.BasicLayoutRegion
38551          */
38552         
38553         /**
38554          * @event beforeremove
38555          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38556          * @param {Roo.LayoutRegion} this
38557          * @param {Roo.ContentPanel} panel The panel
38558          * @param {Object} e The cancel event object
38559          */
38560         "beforeremove" : true,
38561         /**
38562          * @event invalidated
38563          * Fires when the layout for this region is changed.
38564          * @param {Roo.LayoutRegion} this
38565          */
38566         "invalidated" : true,
38567         /**
38568          * @event visibilitychange
38569          * Fires when this region is shown or hidden 
38570          * @param {Roo.LayoutRegion} this
38571          * @param {Boolean} visibility true or false
38572          */
38573         "visibilitychange" : true,
38574         /**
38575          * @event paneladded
38576          * Fires when a panel is added. 
38577          * @param {Roo.LayoutRegion} this
38578          * @param {Roo.ContentPanel} panel The panel
38579          */
38580         "paneladded" : true,
38581         /**
38582          * @event panelremoved
38583          * Fires when a panel is removed. 
38584          * @param {Roo.LayoutRegion} this
38585          * @param {Roo.ContentPanel} panel The panel
38586          */
38587         "panelremoved" : true,
38588         /**
38589          * @event beforecollapse
38590          * Fires when this region before collapse.
38591          * @param {Roo.LayoutRegion} this
38592          */
38593         "beforecollapse" : true,
38594         /**
38595          * @event collapsed
38596          * Fires when this region is collapsed.
38597          * @param {Roo.LayoutRegion} this
38598          */
38599         "collapsed" : true,
38600         /**
38601          * @event expanded
38602          * Fires when this region is expanded.
38603          * @param {Roo.LayoutRegion} this
38604          */
38605         "expanded" : true,
38606         /**
38607          * @event slideshow
38608          * Fires when this region is slid into view.
38609          * @param {Roo.LayoutRegion} this
38610          */
38611         "slideshow" : true,
38612         /**
38613          * @event slidehide
38614          * Fires when this region slides out of view. 
38615          * @param {Roo.LayoutRegion} this
38616          */
38617         "slidehide" : true,
38618         /**
38619          * @event panelactivated
38620          * Fires when a panel is activated. 
38621          * @param {Roo.LayoutRegion} this
38622          * @param {Roo.ContentPanel} panel The activated panel
38623          */
38624         "panelactivated" : true,
38625         /**
38626          * @event resized
38627          * Fires when the user resizes this region. 
38628          * @param {Roo.LayoutRegion} this
38629          * @param {Number} newSize The new size (width for east/west, height for north/south)
38630          */
38631         "resized" : true
38632     };
38633     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38634     this.panels = new Roo.util.MixedCollection();
38635     this.panels.getKey = this.getPanelId.createDelegate(this);
38636     this.box = null;
38637     this.activePanel = null;
38638     // ensure listeners are added...
38639     
38640     if (config.listeners || config.events) {
38641         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38642             listeners : config.listeners || {},
38643             events : config.events || {}
38644         });
38645     }
38646     
38647     if(skipConfig !== true){
38648         this.applyConfig(config);
38649     }
38650 };
38651
38652 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38653 {
38654     getPanelId : function(p){
38655         return p.getId();
38656     },
38657     
38658     applyConfig : function(config){
38659         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38660         this.config = config;
38661         
38662     },
38663     
38664     /**
38665      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38666      * the width, for horizontal (north, south) the height.
38667      * @param {Number} newSize The new width or height
38668      */
38669     resizeTo : function(newSize){
38670         var el = this.el ? this.el :
38671                  (this.activePanel ? this.activePanel.getEl() : null);
38672         if(el){
38673             switch(this.position){
38674                 case "east":
38675                 case "west":
38676                     el.setWidth(newSize);
38677                     this.fireEvent("resized", this, newSize);
38678                 break;
38679                 case "north":
38680                 case "south":
38681                     el.setHeight(newSize);
38682                     this.fireEvent("resized", this, newSize);
38683                 break;                
38684             }
38685         }
38686     },
38687     
38688     getBox : function(){
38689         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38690     },
38691     
38692     getMargins : function(){
38693         return this.margins;
38694     },
38695     
38696     updateBox : function(box){
38697         this.box = box;
38698         var el = this.activePanel.getEl();
38699         el.dom.style.left = box.x + "px";
38700         el.dom.style.top = box.y + "px";
38701         this.activePanel.setSize(box.width, box.height);
38702     },
38703     
38704     /**
38705      * Returns the container element for this region.
38706      * @return {Roo.Element}
38707      */
38708     getEl : function(){
38709         return this.activePanel;
38710     },
38711     
38712     /**
38713      * Returns true if this region is currently visible.
38714      * @return {Boolean}
38715      */
38716     isVisible : function(){
38717         return this.activePanel ? true : false;
38718     },
38719     
38720     setActivePanel : function(panel){
38721         panel = this.getPanel(panel);
38722         if(this.activePanel && this.activePanel != panel){
38723             this.activePanel.setActiveState(false);
38724             this.activePanel.getEl().setLeftTop(-10000,-10000);
38725         }
38726         this.activePanel = panel;
38727         panel.setActiveState(true);
38728         if(this.box){
38729             panel.setSize(this.box.width, this.box.height);
38730         }
38731         this.fireEvent("panelactivated", this, panel);
38732         this.fireEvent("invalidated");
38733     },
38734     
38735     /**
38736      * Show the specified panel.
38737      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38738      * @return {Roo.ContentPanel} The shown panel or null
38739      */
38740     showPanel : function(panel){
38741         panel = this.getPanel(panel);
38742         if(panel){
38743             this.setActivePanel(panel);
38744         }
38745         return panel;
38746     },
38747     
38748     /**
38749      * Get the active panel for this region.
38750      * @return {Roo.ContentPanel} The active panel or null
38751      */
38752     getActivePanel : function(){
38753         return this.activePanel;
38754     },
38755     
38756     /**
38757      * Add the passed ContentPanel(s)
38758      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38759      * @return {Roo.ContentPanel} The panel added (if only one was added)
38760      */
38761     add : function(panel){
38762         if(arguments.length > 1){
38763             for(var i = 0, len = arguments.length; i < len; i++) {
38764                 this.add(arguments[i]);
38765             }
38766             return null;
38767         }
38768         if(this.hasPanel(panel)){
38769             this.showPanel(panel);
38770             return panel;
38771         }
38772         var el = panel.getEl();
38773         if(el.dom.parentNode != this.mgr.el.dom){
38774             this.mgr.el.dom.appendChild(el.dom);
38775         }
38776         if(panel.setRegion){
38777             panel.setRegion(this);
38778         }
38779         this.panels.add(panel);
38780         el.setStyle("position", "absolute");
38781         if(!panel.background){
38782             this.setActivePanel(panel);
38783             if(this.config.initialSize && this.panels.getCount()==1){
38784                 this.resizeTo(this.config.initialSize);
38785             }
38786         }
38787         this.fireEvent("paneladded", this, panel);
38788         return panel;
38789     },
38790     
38791     /**
38792      * Returns true if the panel is in this region.
38793      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38794      * @return {Boolean}
38795      */
38796     hasPanel : function(panel){
38797         if(typeof panel == "object"){ // must be panel obj
38798             panel = panel.getId();
38799         }
38800         return this.getPanel(panel) ? true : false;
38801     },
38802     
38803     /**
38804      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38805      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38806      * @param {Boolean} preservePanel Overrides the config preservePanel option
38807      * @return {Roo.ContentPanel} The panel that was removed
38808      */
38809     remove : function(panel, preservePanel){
38810         panel = this.getPanel(panel);
38811         if(!panel){
38812             return null;
38813         }
38814         var e = {};
38815         this.fireEvent("beforeremove", this, panel, e);
38816         if(e.cancel === true){
38817             return null;
38818         }
38819         var panelId = panel.getId();
38820         this.panels.removeKey(panelId);
38821         return panel;
38822     },
38823     
38824     /**
38825      * Returns the panel specified or null if it's not in this region.
38826      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38827      * @return {Roo.ContentPanel}
38828      */
38829     getPanel : function(id){
38830         if(typeof id == "object"){ // must be panel obj
38831             return id;
38832         }
38833         return this.panels.get(id);
38834     },
38835     
38836     /**
38837      * Returns this regions position (north/south/east/west/center).
38838      * @return {String} 
38839      */
38840     getPosition: function(){
38841         return this.position;    
38842     }
38843 });/*
38844  * Based on:
38845  * Ext JS Library 1.1.1
38846  * Copyright(c) 2006-2007, Ext JS, LLC.
38847  *
38848  * Originally Released Under LGPL - original licence link has changed is not relivant.
38849  *
38850  * Fork - LGPL
38851  * <script type="text/javascript">
38852  */
38853  
38854 /**
38855  * @class Roo.bootstrap.layout.Region
38856  * @extends Roo.bootstrap.layout.Basic
38857  * This class represents a region in a layout manager.
38858  
38859  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38860  * @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})
38861  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38862  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38863  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38864  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38865  * @cfg {String}    title           The title for the region (overrides panel titles)
38866  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38867  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38868  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38869  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38870  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38871  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38872  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38873  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38874  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38875  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38876
38877  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38878  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38879  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38880  * @cfg {Number}    width           For East/West panels
38881  * @cfg {Number}    height          For North/South panels
38882  * @cfg {Boolean}   split           To show the splitter
38883  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38884  * 
38885  * @cfg {string}   cls             Extra CSS classes to add to region
38886  * 
38887  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38888  * @cfg {string}   region  the region that it inhabits..
38889  *
38890
38891  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38892  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38893
38894  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38895  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38896  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38897  */
38898 Roo.bootstrap.layout.Region = function(config)
38899 {
38900     this.applyConfig(config);
38901
38902     var mgr = config.mgr;
38903     var pos = config.region;
38904     config.skipConfig = true;
38905     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38906     
38907     if (mgr.el) {
38908         this.onRender(mgr.el);   
38909     }
38910      
38911     this.visible = true;
38912     this.collapsed = false;
38913     this.unrendered_panels = [];
38914 };
38915
38916 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38917
38918     position: '', // set by wrapper (eg. north/south etc..)
38919     unrendered_panels : null,  // unrendered panels.
38920     
38921     tabPosition : false,
38922     
38923     mgr: false, // points to 'Border'
38924     
38925     
38926     createBody : function(){
38927         /** This region's body element 
38928         * @type Roo.Element */
38929         this.bodyEl = this.el.createChild({
38930                 tag: "div",
38931                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38932         });
38933     },
38934
38935     onRender: function(ctr, pos)
38936     {
38937         var dh = Roo.DomHelper;
38938         /** This region's container element 
38939         * @type Roo.Element */
38940         this.el = dh.append(ctr.dom, {
38941                 tag: "div",
38942                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38943             }, true);
38944         /** This region's title element 
38945         * @type Roo.Element */
38946     
38947         this.titleEl = dh.append(this.el.dom,  {
38948                 tag: "div",
38949                 unselectable: "on",
38950                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38951                 children:[
38952                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38953                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38954                 ]
38955             }, true);
38956         
38957         this.titleEl.enableDisplayMode();
38958         /** This region's title text element 
38959         * @type HTMLElement */
38960         this.titleTextEl = this.titleEl.dom.firstChild;
38961         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38962         /*
38963         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38964         this.closeBtn.enableDisplayMode();
38965         this.closeBtn.on("click", this.closeClicked, this);
38966         this.closeBtn.hide();
38967     */
38968         this.createBody(this.config);
38969         if(this.config.hideWhenEmpty){
38970             this.hide();
38971             this.on("paneladded", this.validateVisibility, this);
38972             this.on("panelremoved", this.validateVisibility, this);
38973         }
38974         if(this.autoScroll){
38975             this.bodyEl.setStyle("overflow", "auto");
38976         }else{
38977             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38978         }
38979         //if(c.titlebar !== false){
38980             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38981                 this.titleEl.hide();
38982             }else{
38983                 this.titleEl.show();
38984                 if(this.config.title){
38985                     this.titleTextEl.innerHTML = this.config.title;
38986                 }
38987             }
38988         //}
38989         if(this.config.collapsed){
38990             this.collapse(true);
38991         }
38992         if(this.config.hidden){
38993             this.hide();
38994         }
38995         
38996         if (this.unrendered_panels && this.unrendered_panels.length) {
38997             for (var i =0;i< this.unrendered_panels.length; i++) {
38998                 this.add(this.unrendered_panels[i]);
38999             }
39000             this.unrendered_panels = null;
39001             
39002         }
39003         
39004     },
39005     
39006     applyConfig : function(c)
39007     {
39008         /*
39009          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39010             var dh = Roo.DomHelper;
39011             if(c.titlebar !== false){
39012                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39013                 this.collapseBtn.on("click", this.collapse, this);
39014                 this.collapseBtn.enableDisplayMode();
39015                 /*
39016                 if(c.showPin === true || this.showPin){
39017                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39018                     this.stickBtn.enableDisplayMode();
39019                     this.stickBtn.on("click", this.expand, this);
39020                     this.stickBtn.hide();
39021                 }
39022                 
39023             }
39024             */
39025             /** This region's collapsed element
39026             * @type Roo.Element */
39027             /*
39028              *
39029             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39030                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39031             ]}, true);
39032             
39033             if(c.floatable !== false){
39034                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39035                this.collapsedEl.on("click", this.collapseClick, this);
39036             }
39037
39038             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39039                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39040                    id: "message", unselectable: "on", style:{"float":"left"}});
39041                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39042              }
39043             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39044             this.expandBtn.on("click", this.expand, this);
39045             
39046         }
39047         
39048         if(this.collapseBtn){
39049             this.collapseBtn.setVisible(c.collapsible == true);
39050         }
39051         
39052         this.cmargins = c.cmargins || this.cmargins ||
39053                          (this.position == "west" || this.position == "east" ?
39054                              {top: 0, left: 2, right:2, bottom: 0} :
39055                              {top: 2, left: 0, right:0, bottom: 2});
39056         */
39057         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39058         
39059         
39060         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39061         
39062         this.autoScroll = c.autoScroll || false;
39063         
39064         
39065        
39066         
39067         this.duration = c.duration || .30;
39068         this.slideDuration = c.slideDuration || .45;
39069         this.config = c;
39070        
39071     },
39072     /**
39073      * Returns true if this region is currently visible.
39074      * @return {Boolean}
39075      */
39076     isVisible : function(){
39077         return this.visible;
39078     },
39079
39080     /**
39081      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39082      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39083      */
39084     //setCollapsedTitle : function(title){
39085     //    title = title || "&#160;";
39086      //   if(this.collapsedTitleTextEl){
39087       //      this.collapsedTitleTextEl.innerHTML = title;
39088        // }
39089     //},
39090
39091     getBox : function(){
39092         var b;
39093       //  if(!this.collapsed){
39094             b = this.el.getBox(false, true);
39095        // }else{
39096           //  b = this.collapsedEl.getBox(false, true);
39097         //}
39098         return b;
39099     },
39100
39101     getMargins : function(){
39102         return this.margins;
39103         //return this.collapsed ? this.cmargins : this.margins;
39104     },
39105 /*
39106     highlight : function(){
39107         this.el.addClass("x-layout-panel-dragover");
39108     },
39109
39110     unhighlight : function(){
39111         this.el.removeClass("x-layout-panel-dragover");
39112     },
39113 */
39114     updateBox : function(box)
39115     {
39116         if (!this.bodyEl) {
39117             return; // not rendered yet..
39118         }
39119         
39120         this.box = box;
39121         if(!this.collapsed){
39122             this.el.dom.style.left = box.x + "px";
39123             this.el.dom.style.top = box.y + "px";
39124             this.updateBody(box.width, box.height);
39125         }else{
39126             this.collapsedEl.dom.style.left = box.x + "px";
39127             this.collapsedEl.dom.style.top = box.y + "px";
39128             this.collapsedEl.setSize(box.width, box.height);
39129         }
39130         if(this.tabs){
39131             this.tabs.autoSizeTabs();
39132         }
39133     },
39134
39135     updateBody : function(w, h)
39136     {
39137         if(w !== null){
39138             this.el.setWidth(w);
39139             w -= this.el.getBorderWidth("rl");
39140             if(this.config.adjustments){
39141                 w += this.config.adjustments[0];
39142             }
39143         }
39144         if(h !== null && h > 0){
39145             this.el.setHeight(h);
39146             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39147             h -= this.el.getBorderWidth("tb");
39148             if(this.config.adjustments){
39149                 h += this.config.adjustments[1];
39150             }
39151             this.bodyEl.setHeight(h);
39152             if(this.tabs){
39153                 h = this.tabs.syncHeight(h);
39154             }
39155         }
39156         if(this.panelSize){
39157             w = w !== null ? w : this.panelSize.width;
39158             h = h !== null ? h : this.panelSize.height;
39159         }
39160         if(this.activePanel){
39161             var el = this.activePanel.getEl();
39162             w = w !== null ? w : el.getWidth();
39163             h = h !== null ? h : el.getHeight();
39164             this.panelSize = {width: w, height: h};
39165             this.activePanel.setSize(w, h);
39166         }
39167         if(Roo.isIE && this.tabs){
39168             this.tabs.el.repaint();
39169         }
39170     },
39171
39172     /**
39173      * Returns the container element for this region.
39174      * @return {Roo.Element}
39175      */
39176     getEl : function(){
39177         return this.el;
39178     },
39179
39180     /**
39181      * Hides this region.
39182      */
39183     hide : function(){
39184         //if(!this.collapsed){
39185             this.el.dom.style.left = "-2000px";
39186             this.el.hide();
39187         //}else{
39188          //   this.collapsedEl.dom.style.left = "-2000px";
39189          //   this.collapsedEl.hide();
39190        // }
39191         this.visible = false;
39192         this.fireEvent("visibilitychange", this, false);
39193     },
39194
39195     /**
39196      * Shows this region if it was previously hidden.
39197      */
39198     show : function(){
39199         //if(!this.collapsed){
39200             this.el.show();
39201         //}else{
39202         //    this.collapsedEl.show();
39203        // }
39204         this.visible = true;
39205         this.fireEvent("visibilitychange", this, true);
39206     },
39207 /*
39208     closeClicked : function(){
39209         if(this.activePanel){
39210             this.remove(this.activePanel);
39211         }
39212     },
39213
39214     collapseClick : function(e){
39215         if(this.isSlid){
39216            e.stopPropagation();
39217            this.slideIn();
39218         }else{
39219            e.stopPropagation();
39220            this.slideOut();
39221         }
39222     },
39223 */
39224     /**
39225      * Collapses this region.
39226      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39227      */
39228     /*
39229     collapse : function(skipAnim, skipCheck = false){
39230         if(this.collapsed) {
39231             return;
39232         }
39233         
39234         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39235             
39236             this.collapsed = true;
39237             if(this.split){
39238                 this.split.el.hide();
39239             }
39240             if(this.config.animate && skipAnim !== true){
39241                 this.fireEvent("invalidated", this);
39242                 this.animateCollapse();
39243             }else{
39244                 this.el.setLocation(-20000,-20000);
39245                 this.el.hide();
39246                 this.collapsedEl.show();
39247                 this.fireEvent("collapsed", this);
39248                 this.fireEvent("invalidated", this);
39249             }
39250         }
39251         
39252     },
39253 */
39254     animateCollapse : function(){
39255         // overridden
39256     },
39257
39258     /**
39259      * Expands this region if it was previously collapsed.
39260      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39261      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39262      */
39263     /*
39264     expand : function(e, skipAnim){
39265         if(e) {
39266             e.stopPropagation();
39267         }
39268         if(!this.collapsed || this.el.hasActiveFx()) {
39269             return;
39270         }
39271         if(this.isSlid){
39272             this.afterSlideIn();
39273             skipAnim = true;
39274         }
39275         this.collapsed = false;
39276         if(this.config.animate && skipAnim !== true){
39277             this.animateExpand();
39278         }else{
39279             this.el.show();
39280             if(this.split){
39281                 this.split.el.show();
39282             }
39283             this.collapsedEl.setLocation(-2000,-2000);
39284             this.collapsedEl.hide();
39285             this.fireEvent("invalidated", this);
39286             this.fireEvent("expanded", this);
39287         }
39288     },
39289 */
39290     animateExpand : function(){
39291         // overridden
39292     },
39293
39294     initTabs : function()
39295     {
39296         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39297         
39298         var ts = new Roo.bootstrap.panel.Tabs({
39299             el: this.bodyEl.dom,
39300             region : this,
39301             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39302             disableTooltips: this.config.disableTabTips,
39303             toolbar : this.config.toolbar
39304         });
39305         
39306         if(this.config.hideTabs){
39307             ts.stripWrap.setDisplayed(false);
39308         }
39309         this.tabs = ts;
39310         ts.resizeTabs = this.config.resizeTabs === true;
39311         ts.minTabWidth = this.config.minTabWidth || 40;
39312         ts.maxTabWidth = this.config.maxTabWidth || 250;
39313         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39314         ts.monitorResize = false;
39315         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39316         ts.bodyEl.addClass('roo-layout-tabs-body');
39317         this.panels.each(this.initPanelAsTab, this);
39318     },
39319
39320     initPanelAsTab : function(panel){
39321         var ti = this.tabs.addTab(
39322             panel.getEl().id,
39323             panel.getTitle(),
39324             null,
39325             this.config.closeOnTab && panel.isClosable(),
39326             panel.tpl
39327         );
39328         if(panel.tabTip !== undefined){
39329             ti.setTooltip(panel.tabTip);
39330         }
39331         ti.on("activate", function(){
39332               this.setActivePanel(panel);
39333         }, this);
39334         
39335         if(this.config.closeOnTab){
39336             ti.on("beforeclose", function(t, e){
39337                 e.cancel = true;
39338                 this.remove(panel);
39339             }, this);
39340         }
39341         
39342         panel.tabItem = ti;
39343         
39344         return ti;
39345     },
39346
39347     updatePanelTitle : function(panel, title)
39348     {
39349         if(this.activePanel == panel){
39350             this.updateTitle(title);
39351         }
39352         if(this.tabs){
39353             var ti = this.tabs.getTab(panel.getEl().id);
39354             ti.setText(title);
39355             if(panel.tabTip !== undefined){
39356                 ti.setTooltip(panel.tabTip);
39357             }
39358         }
39359     },
39360
39361     updateTitle : function(title){
39362         if(this.titleTextEl && !this.config.title){
39363             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39364         }
39365     },
39366
39367     setActivePanel : function(panel)
39368     {
39369         panel = this.getPanel(panel);
39370         if(this.activePanel && this.activePanel != panel){
39371             if(this.activePanel.setActiveState(false) === false){
39372                 return;
39373             }
39374         }
39375         this.activePanel = panel;
39376         panel.setActiveState(true);
39377         if(this.panelSize){
39378             panel.setSize(this.panelSize.width, this.panelSize.height);
39379         }
39380         if(this.closeBtn){
39381             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39382         }
39383         this.updateTitle(panel.getTitle());
39384         if(this.tabs){
39385             this.fireEvent("invalidated", this);
39386         }
39387         this.fireEvent("panelactivated", this, panel);
39388     },
39389
39390     /**
39391      * Shows the specified panel.
39392      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39393      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39394      */
39395     showPanel : function(panel)
39396     {
39397         panel = this.getPanel(panel);
39398         if(panel){
39399             if(this.tabs){
39400                 var tab = this.tabs.getTab(panel.getEl().id);
39401                 if(tab.isHidden()){
39402                     this.tabs.unhideTab(tab.id);
39403                 }
39404                 tab.activate();
39405             }else{
39406                 this.setActivePanel(panel);
39407             }
39408         }
39409         return panel;
39410     },
39411
39412     /**
39413      * Get the active panel for this region.
39414      * @return {Roo.ContentPanel} The active panel or null
39415      */
39416     getActivePanel : function(){
39417         return this.activePanel;
39418     },
39419
39420     validateVisibility : function(){
39421         if(this.panels.getCount() < 1){
39422             this.updateTitle("&#160;");
39423             this.closeBtn.hide();
39424             this.hide();
39425         }else{
39426             if(!this.isVisible()){
39427                 this.show();
39428             }
39429         }
39430     },
39431
39432     /**
39433      * Adds the passed ContentPanel(s) to this region.
39434      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39435      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39436      */
39437     add : function(panel)
39438     {
39439         if(arguments.length > 1){
39440             for(var i = 0, len = arguments.length; i < len; i++) {
39441                 this.add(arguments[i]);
39442             }
39443             return null;
39444         }
39445         
39446         // if we have not been rendered yet, then we can not really do much of this..
39447         if (!this.bodyEl) {
39448             this.unrendered_panels.push(panel);
39449             return panel;
39450         }
39451         
39452         
39453         
39454         
39455         if(this.hasPanel(panel)){
39456             this.showPanel(panel);
39457             return panel;
39458         }
39459         panel.setRegion(this);
39460         this.panels.add(panel);
39461        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39462             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39463             // and hide them... ???
39464             this.bodyEl.dom.appendChild(panel.getEl().dom);
39465             if(panel.background !== true){
39466                 this.setActivePanel(panel);
39467             }
39468             this.fireEvent("paneladded", this, panel);
39469             return panel;
39470         }
39471         */
39472         if(!this.tabs){
39473             this.initTabs();
39474         }else{
39475             this.initPanelAsTab(panel);
39476         }
39477         
39478         
39479         if(panel.background !== true){
39480             this.tabs.activate(panel.getEl().id);
39481         }
39482         this.fireEvent("paneladded", this, panel);
39483         return panel;
39484     },
39485
39486     /**
39487      * Hides the tab for the specified panel.
39488      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39489      */
39490     hidePanel : function(panel){
39491         if(this.tabs && (panel = this.getPanel(panel))){
39492             this.tabs.hideTab(panel.getEl().id);
39493         }
39494     },
39495
39496     /**
39497      * Unhides the tab for a previously hidden panel.
39498      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39499      */
39500     unhidePanel : function(panel){
39501         if(this.tabs && (panel = this.getPanel(panel))){
39502             this.tabs.unhideTab(panel.getEl().id);
39503         }
39504     },
39505
39506     clearPanels : function(){
39507         while(this.panels.getCount() > 0){
39508              this.remove(this.panels.first());
39509         }
39510     },
39511
39512     /**
39513      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39514      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39515      * @param {Boolean} preservePanel Overrides the config preservePanel option
39516      * @return {Roo.ContentPanel} The panel that was removed
39517      */
39518     remove : function(panel, preservePanel)
39519     {
39520         panel = this.getPanel(panel);
39521         if(!panel){
39522             return null;
39523         }
39524         var e = {};
39525         this.fireEvent("beforeremove", this, panel, e);
39526         if(e.cancel === true){
39527             return null;
39528         }
39529         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39530         var panelId = panel.getId();
39531         this.panels.removeKey(panelId);
39532         if(preservePanel){
39533             document.body.appendChild(panel.getEl().dom);
39534         }
39535         if(this.tabs){
39536             this.tabs.removeTab(panel.getEl().id);
39537         }else if (!preservePanel){
39538             this.bodyEl.dom.removeChild(panel.getEl().dom);
39539         }
39540         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39541             var p = this.panels.first();
39542             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39543             tempEl.appendChild(p.getEl().dom);
39544             this.bodyEl.update("");
39545             this.bodyEl.dom.appendChild(p.getEl().dom);
39546             tempEl = null;
39547             this.updateTitle(p.getTitle());
39548             this.tabs = null;
39549             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39550             this.setActivePanel(p);
39551         }
39552         panel.setRegion(null);
39553         if(this.activePanel == panel){
39554             this.activePanel = null;
39555         }
39556         if(this.config.autoDestroy !== false && preservePanel !== true){
39557             try{panel.destroy();}catch(e){}
39558         }
39559         this.fireEvent("panelremoved", this, panel);
39560         return panel;
39561     },
39562
39563     /**
39564      * Returns the TabPanel component used by this region
39565      * @return {Roo.TabPanel}
39566      */
39567     getTabs : function(){
39568         return this.tabs;
39569     },
39570
39571     createTool : function(parentEl, className){
39572         var btn = Roo.DomHelper.append(parentEl, {
39573             tag: "div",
39574             cls: "x-layout-tools-button",
39575             children: [ {
39576                 tag: "div",
39577                 cls: "roo-layout-tools-button-inner " + className,
39578                 html: "&#160;"
39579             }]
39580         }, true);
39581         btn.addClassOnOver("roo-layout-tools-button-over");
39582         return btn;
39583     }
39584 });/*
39585  * Based on:
39586  * Ext JS Library 1.1.1
39587  * Copyright(c) 2006-2007, Ext JS, LLC.
39588  *
39589  * Originally Released Under LGPL - original licence link has changed is not relivant.
39590  *
39591  * Fork - LGPL
39592  * <script type="text/javascript">
39593  */
39594  
39595
39596
39597 /**
39598  * @class Roo.SplitLayoutRegion
39599  * @extends Roo.LayoutRegion
39600  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39601  */
39602 Roo.bootstrap.layout.Split = function(config){
39603     this.cursor = config.cursor;
39604     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39605 };
39606
39607 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39608 {
39609     splitTip : "Drag to resize.",
39610     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39611     useSplitTips : false,
39612
39613     applyConfig : function(config){
39614         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39615     },
39616     
39617     onRender : function(ctr,pos) {
39618         
39619         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39620         if(!this.config.split){
39621             return;
39622         }
39623         if(!this.split){
39624             
39625             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39626                             tag: "div",
39627                             id: this.el.id + "-split",
39628                             cls: "roo-layout-split roo-layout-split-"+this.position,
39629                             html: "&#160;"
39630             });
39631             /** The SplitBar for this region 
39632             * @type Roo.SplitBar */
39633             // does not exist yet...
39634             Roo.log([this.position, this.orientation]);
39635             
39636             this.split = new Roo.bootstrap.SplitBar({
39637                 dragElement : splitEl,
39638                 resizingElement: this.el,
39639                 orientation : this.orientation
39640             });
39641             
39642             this.split.on("moved", this.onSplitMove, this);
39643             this.split.useShim = this.config.useShim === true;
39644             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39645             if(this.useSplitTips){
39646                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39647             }
39648             //if(config.collapsible){
39649             //    this.split.el.on("dblclick", this.collapse,  this);
39650             //}
39651         }
39652         if(typeof this.config.minSize != "undefined"){
39653             this.split.minSize = this.config.minSize;
39654         }
39655         if(typeof this.config.maxSize != "undefined"){
39656             this.split.maxSize = this.config.maxSize;
39657         }
39658         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39659             this.hideSplitter();
39660         }
39661         
39662     },
39663
39664     getHMaxSize : function(){
39665          var cmax = this.config.maxSize || 10000;
39666          var center = this.mgr.getRegion("center");
39667          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39668     },
39669
39670     getVMaxSize : function(){
39671          var cmax = this.config.maxSize || 10000;
39672          var center = this.mgr.getRegion("center");
39673          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39674     },
39675
39676     onSplitMove : function(split, newSize){
39677         this.fireEvent("resized", this, newSize);
39678     },
39679     
39680     /** 
39681      * Returns the {@link Roo.SplitBar} for this region.
39682      * @return {Roo.SplitBar}
39683      */
39684     getSplitBar : function(){
39685         return this.split;
39686     },
39687     
39688     hide : function(){
39689         this.hideSplitter();
39690         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39691     },
39692
39693     hideSplitter : function(){
39694         if(this.split){
39695             this.split.el.setLocation(-2000,-2000);
39696             this.split.el.hide();
39697         }
39698     },
39699
39700     show : function(){
39701         if(this.split){
39702             this.split.el.show();
39703         }
39704         Roo.bootstrap.layout.Split.superclass.show.call(this);
39705     },
39706     
39707     beforeSlide: function(){
39708         if(Roo.isGecko){// firefox overflow auto bug workaround
39709             this.bodyEl.clip();
39710             if(this.tabs) {
39711                 this.tabs.bodyEl.clip();
39712             }
39713             if(this.activePanel){
39714                 this.activePanel.getEl().clip();
39715                 
39716                 if(this.activePanel.beforeSlide){
39717                     this.activePanel.beforeSlide();
39718                 }
39719             }
39720         }
39721     },
39722     
39723     afterSlide : function(){
39724         if(Roo.isGecko){// firefox overflow auto bug workaround
39725             this.bodyEl.unclip();
39726             if(this.tabs) {
39727                 this.tabs.bodyEl.unclip();
39728             }
39729             if(this.activePanel){
39730                 this.activePanel.getEl().unclip();
39731                 if(this.activePanel.afterSlide){
39732                     this.activePanel.afterSlide();
39733                 }
39734             }
39735         }
39736     },
39737
39738     initAutoHide : function(){
39739         if(this.autoHide !== false){
39740             if(!this.autoHideHd){
39741                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39742                 this.autoHideHd = {
39743                     "mouseout": function(e){
39744                         if(!e.within(this.el, true)){
39745                             st.delay(500);
39746                         }
39747                     },
39748                     "mouseover" : function(e){
39749                         st.cancel();
39750                     },
39751                     scope : this
39752                 };
39753             }
39754             this.el.on(this.autoHideHd);
39755         }
39756     },
39757
39758     clearAutoHide : function(){
39759         if(this.autoHide !== false){
39760             this.el.un("mouseout", this.autoHideHd.mouseout);
39761             this.el.un("mouseover", this.autoHideHd.mouseover);
39762         }
39763     },
39764
39765     clearMonitor : function(){
39766         Roo.get(document).un("click", this.slideInIf, this);
39767     },
39768
39769     // these names are backwards but not changed for compat
39770     slideOut : function(){
39771         if(this.isSlid || this.el.hasActiveFx()){
39772             return;
39773         }
39774         this.isSlid = true;
39775         if(this.collapseBtn){
39776             this.collapseBtn.hide();
39777         }
39778         this.closeBtnState = this.closeBtn.getStyle('display');
39779         this.closeBtn.hide();
39780         if(this.stickBtn){
39781             this.stickBtn.show();
39782         }
39783         this.el.show();
39784         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39785         this.beforeSlide();
39786         this.el.setStyle("z-index", 10001);
39787         this.el.slideIn(this.getSlideAnchor(), {
39788             callback: function(){
39789                 this.afterSlide();
39790                 this.initAutoHide();
39791                 Roo.get(document).on("click", this.slideInIf, this);
39792                 this.fireEvent("slideshow", this);
39793             },
39794             scope: this,
39795             block: true
39796         });
39797     },
39798
39799     afterSlideIn : function(){
39800         this.clearAutoHide();
39801         this.isSlid = false;
39802         this.clearMonitor();
39803         this.el.setStyle("z-index", "");
39804         if(this.collapseBtn){
39805             this.collapseBtn.show();
39806         }
39807         this.closeBtn.setStyle('display', this.closeBtnState);
39808         if(this.stickBtn){
39809             this.stickBtn.hide();
39810         }
39811         this.fireEvent("slidehide", this);
39812     },
39813
39814     slideIn : function(cb){
39815         if(!this.isSlid || this.el.hasActiveFx()){
39816             Roo.callback(cb);
39817             return;
39818         }
39819         this.isSlid = false;
39820         this.beforeSlide();
39821         this.el.slideOut(this.getSlideAnchor(), {
39822             callback: function(){
39823                 this.el.setLeftTop(-10000, -10000);
39824                 this.afterSlide();
39825                 this.afterSlideIn();
39826                 Roo.callback(cb);
39827             },
39828             scope: this,
39829             block: true
39830         });
39831     },
39832     
39833     slideInIf : function(e){
39834         if(!e.within(this.el)){
39835             this.slideIn();
39836         }
39837     },
39838
39839     animateCollapse : function(){
39840         this.beforeSlide();
39841         this.el.setStyle("z-index", 20000);
39842         var anchor = this.getSlideAnchor();
39843         this.el.slideOut(anchor, {
39844             callback : function(){
39845                 this.el.setStyle("z-index", "");
39846                 this.collapsedEl.slideIn(anchor, {duration:.3});
39847                 this.afterSlide();
39848                 this.el.setLocation(-10000,-10000);
39849                 this.el.hide();
39850                 this.fireEvent("collapsed", this);
39851             },
39852             scope: this,
39853             block: true
39854         });
39855     },
39856
39857     animateExpand : function(){
39858         this.beforeSlide();
39859         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39860         this.el.setStyle("z-index", 20000);
39861         this.collapsedEl.hide({
39862             duration:.1
39863         });
39864         this.el.slideIn(this.getSlideAnchor(), {
39865             callback : function(){
39866                 this.el.setStyle("z-index", "");
39867                 this.afterSlide();
39868                 if(this.split){
39869                     this.split.el.show();
39870                 }
39871                 this.fireEvent("invalidated", this);
39872                 this.fireEvent("expanded", this);
39873             },
39874             scope: this,
39875             block: true
39876         });
39877     },
39878
39879     anchors : {
39880         "west" : "left",
39881         "east" : "right",
39882         "north" : "top",
39883         "south" : "bottom"
39884     },
39885
39886     sanchors : {
39887         "west" : "l",
39888         "east" : "r",
39889         "north" : "t",
39890         "south" : "b"
39891     },
39892
39893     canchors : {
39894         "west" : "tl-tr",
39895         "east" : "tr-tl",
39896         "north" : "tl-bl",
39897         "south" : "bl-tl"
39898     },
39899
39900     getAnchor : function(){
39901         return this.anchors[this.position];
39902     },
39903
39904     getCollapseAnchor : function(){
39905         return this.canchors[this.position];
39906     },
39907
39908     getSlideAnchor : function(){
39909         return this.sanchors[this.position];
39910     },
39911
39912     getAlignAdj : function(){
39913         var cm = this.cmargins;
39914         switch(this.position){
39915             case "west":
39916                 return [0, 0];
39917             break;
39918             case "east":
39919                 return [0, 0];
39920             break;
39921             case "north":
39922                 return [0, 0];
39923             break;
39924             case "south":
39925                 return [0, 0];
39926             break;
39927         }
39928     },
39929
39930     getExpandAdj : function(){
39931         var c = this.collapsedEl, cm = this.cmargins;
39932         switch(this.position){
39933             case "west":
39934                 return [-(cm.right+c.getWidth()+cm.left), 0];
39935             break;
39936             case "east":
39937                 return [cm.right+c.getWidth()+cm.left, 0];
39938             break;
39939             case "north":
39940                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39941             break;
39942             case "south":
39943                 return [0, cm.top+cm.bottom+c.getHeight()];
39944             break;
39945         }
39946     }
39947 });/*
39948  * Based on:
39949  * Ext JS Library 1.1.1
39950  * Copyright(c) 2006-2007, Ext JS, LLC.
39951  *
39952  * Originally Released Under LGPL - original licence link has changed is not relivant.
39953  *
39954  * Fork - LGPL
39955  * <script type="text/javascript">
39956  */
39957 /*
39958  * These classes are private internal classes
39959  */
39960 Roo.bootstrap.layout.Center = function(config){
39961     config.region = "center";
39962     Roo.bootstrap.layout.Region.call(this, config);
39963     this.visible = true;
39964     this.minWidth = config.minWidth || 20;
39965     this.minHeight = config.minHeight || 20;
39966 };
39967
39968 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39969     hide : function(){
39970         // center panel can't be hidden
39971     },
39972     
39973     show : function(){
39974         // center panel can't be hidden
39975     },
39976     
39977     getMinWidth: function(){
39978         return this.minWidth;
39979     },
39980     
39981     getMinHeight: function(){
39982         return this.minHeight;
39983     }
39984 });
39985
39986
39987
39988
39989  
39990
39991
39992
39993
39994
39995
39996 Roo.bootstrap.layout.North = function(config)
39997 {
39998     config.region = 'north';
39999     config.cursor = 'n-resize';
40000     
40001     Roo.bootstrap.layout.Split.call(this, config);
40002     
40003     
40004     if(this.split){
40005         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40006         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40007         this.split.el.addClass("roo-layout-split-v");
40008     }
40009     //var size = config.initialSize || config.height;
40010     //if(this.el && typeof size != "undefined"){
40011     //    this.el.setHeight(size);
40012     //}
40013 };
40014 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40015 {
40016     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40017      
40018      
40019     onRender : function(ctr, pos)
40020     {
40021         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40022         var size = this.config.initialSize || this.config.height;
40023         if(this.el && typeof size != "undefined"){
40024             this.el.setHeight(size);
40025         }
40026     
40027     },
40028     
40029     getBox : function(){
40030         if(this.collapsed){
40031             return this.collapsedEl.getBox();
40032         }
40033         var box = this.el.getBox();
40034         if(this.split){
40035             box.height += this.split.el.getHeight();
40036         }
40037         return box;
40038     },
40039     
40040     updateBox : function(box){
40041         if(this.split && !this.collapsed){
40042             box.height -= this.split.el.getHeight();
40043             this.split.el.setLeft(box.x);
40044             this.split.el.setTop(box.y+box.height);
40045             this.split.el.setWidth(box.width);
40046         }
40047         if(this.collapsed){
40048             this.updateBody(box.width, null);
40049         }
40050         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40051     }
40052 });
40053
40054
40055
40056
40057
40058 Roo.bootstrap.layout.South = function(config){
40059     config.region = 'south';
40060     config.cursor = 's-resize';
40061     Roo.bootstrap.layout.Split.call(this, config);
40062     if(this.split){
40063         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40064         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40065         this.split.el.addClass("roo-layout-split-v");
40066     }
40067     
40068 };
40069
40070 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40071     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40072     
40073     onRender : function(ctr, pos)
40074     {
40075         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40076         var size = this.config.initialSize || this.config.height;
40077         if(this.el && typeof size != "undefined"){
40078             this.el.setHeight(size);
40079         }
40080     
40081     },
40082     
40083     getBox : function(){
40084         if(this.collapsed){
40085             return this.collapsedEl.getBox();
40086         }
40087         var box = this.el.getBox();
40088         if(this.split){
40089             var sh = this.split.el.getHeight();
40090             box.height += sh;
40091             box.y -= sh;
40092         }
40093         return box;
40094     },
40095     
40096     updateBox : function(box){
40097         if(this.split && !this.collapsed){
40098             var sh = this.split.el.getHeight();
40099             box.height -= sh;
40100             box.y += sh;
40101             this.split.el.setLeft(box.x);
40102             this.split.el.setTop(box.y-sh);
40103             this.split.el.setWidth(box.width);
40104         }
40105         if(this.collapsed){
40106             this.updateBody(box.width, null);
40107         }
40108         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40109     }
40110 });
40111
40112 Roo.bootstrap.layout.East = function(config){
40113     config.region = "east";
40114     config.cursor = "e-resize";
40115     Roo.bootstrap.layout.Split.call(this, config);
40116     if(this.split){
40117         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40118         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40119         this.split.el.addClass("roo-layout-split-h");
40120     }
40121     
40122 };
40123 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40124     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40125     
40126     onRender : function(ctr, pos)
40127     {
40128         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40129         var size = this.config.initialSize || this.config.width;
40130         if(this.el && typeof size != "undefined"){
40131             this.el.setWidth(size);
40132         }
40133     
40134     },
40135     
40136     getBox : function(){
40137         if(this.collapsed){
40138             return this.collapsedEl.getBox();
40139         }
40140         var box = this.el.getBox();
40141         if(this.split){
40142             var sw = this.split.el.getWidth();
40143             box.width += sw;
40144             box.x -= sw;
40145         }
40146         return box;
40147     },
40148
40149     updateBox : function(box){
40150         if(this.split && !this.collapsed){
40151             var sw = this.split.el.getWidth();
40152             box.width -= sw;
40153             this.split.el.setLeft(box.x);
40154             this.split.el.setTop(box.y);
40155             this.split.el.setHeight(box.height);
40156             box.x += sw;
40157         }
40158         if(this.collapsed){
40159             this.updateBody(null, box.height);
40160         }
40161         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40162     }
40163 });
40164
40165 Roo.bootstrap.layout.West = function(config){
40166     config.region = "west";
40167     config.cursor = "w-resize";
40168     
40169     Roo.bootstrap.layout.Split.call(this, config);
40170     if(this.split){
40171         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40172         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40173         this.split.el.addClass("roo-layout-split-h");
40174     }
40175     
40176 };
40177 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40178     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40179     
40180     onRender: function(ctr, pos)
40181     {
40182         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40183         var size = this.config.initialSize || this.config.width;
40184         if(typeof size != "undefined"){
40185             this.el.setWidth(size);
40186         }
40187     },
40188     
40189     getBox : function(){
40190         if(this.collapsed){
40191             return this.collapsedEl.getBox();
40192         }
40193         var box = this.el.getBox();
40194         if (box.width == 0) {
40195             box.width = this.config.width; // kludge?
40196         }
40197         if(this.split){
40198             box.width += this.split.el.getWidth();
40199         }
40200         return box;
40201     },
40202     
40203     updateBox : function(box){
40204         if(this.split && !this.collapsed){
40205             var sw = this.split.el.getWidth();
40206             box.width -= sw;
40207             this.split.el.setLeft(box.x+box.width);
40208             this.split.el.setTop(box.y);
40209             this.split.el.setHeight(box.height);
40210         }
40211         if(this.collapsed){
40212             this.updateBody(null, box.height);
40213         }
40214         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40215     }
40216 });/*
40217  * Based on:
40218  * Ext JS Library 1.1.1
40219  * Copyright(c) 2006-2007, Ext JS, LLC.
40220  *
40221  * Originally Released Under LGPL - original licence link has changed is not relivant.
40222  *
40223  * Fork - LGPL
40224  * <script type="text/javascript">
40225  */
40226 /**
40227  * @class Roo.bootstrap.paenl.Content
40228  * @extends Roo.util.Observable
40229  * @children Roo.bootstrap.Component
40230  * @parent builder Roo.bootstrap.layout.Border
40231  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40232  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40233  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40234  * @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
40235  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40236  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40237  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40238  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40239  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40240  * @cfg {String} title          The title for this panel
40241  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40242  * @cfg {String} url            Calls {@link #setUrl} with this value
40243  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40244  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40245  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40246  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40247  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40248  * @cfg {Boolean} badges render the badges
40249  * @cfg {String} cls  extra classes to use  
40250  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40251  
40252  * @constructor
40253  * Create a new ContentPanel.
40254  * @param {String/Object} config A string to set only the title or a config object
40255  
40256  */
40257 Roo.bootstrap.panel.Content = function( config){
40258     
40259     this.tpl = config.tpl || false;
40260     
40261     var el = config.el;
40262     var content = config.content;
40263
40264     if(config.autoCreate){ // xtype is available if this is called from factory
40265         el = Roo.id();
40266     }
40267     this.el = Roo.get(el);
40268     if(!this.el && config && config.autoCreate){
40269         if(typeof config.autoCreate == "object"){
40270             if(!config.autoCreate.id){
40271                 config.autoCreate.id = config.id||el;
40272             }
40273             this.el = Roo.DomHelper.append(document.body,
40274                         config.autoCreate, true);
40275         }else{
40276             var elcfg =  {
40277                 tag: "div",
40278                 cls: (config.cls || '') +
40279                     (config.background ? ' bg-' + config.background : '') +
40280                     " roo-layout-inactive-content",
40281                 id: config.id||el
40282             };
40283             if (config.iframe) {
40284                 elcfg.cn = [
40285                     {
40286                         tag : 'iframe',
40287                         style : 'border: 0px',
40288                         src : 'about:blank'
40289                     }
40290                 ];
40291             }
40292               
40293             if (config.html) {
40294                 elcfg.html = config.html;
40295                 
40296             }
40297                         
40298             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40299             if (config.iframe) {
40300                 this.iframeEl = this.el.select('iframe',true).first();
40301             }
40302             
40303         }
40304     } 
40305     this.closable = false;
40306     this.loaded = false;
40307     this.active = false;
40308    
40309       
40310     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40311         
40312         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40313         
40314         this.wrapEl = this.el; //this.el.wrap();
40315         var ti = [];
40316         if (config.toolbar.items) {
40317             ti = config.toolbar.items ;
40318             delete config.toolbar.items ;
40319         }
40320         
40321         var nitems = [];
40322         this.toolbar.render(this.wrapEl, 'before');
40323         for(var i =0;i < ti.length;i++) {
40324           //  Roo.log(['add child', items[i]]);
40325             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40326         }
40327         this.toolbar.items = nitems;
40328         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40329         delete config.toolbar;
40330         
40331     }
40332     /*
40333     // xtype created footer. - not sure if will work as we normally have to render first..
40334     if (this.footer && !this.footer.el && this.footer.xtype) {
40335         if (!this.wrapEl) {
40336             this.wrapEl = this.el.wrap();
40337         }
40338     
40339         this.footer.container = this.wrapEl.createChild();
40340          
40341         this.footer = Roo.factory(this.footer, Roo);
40342         
40343     }
40344     */
40345     
40346      if(typeof config == "string"){
40347         this.title = config;
40348     }else{
40349         Roo.apply(this, config);
40350     }
40351     
40352     if(this.resizeEl){
40353         this.resizeEl = Roo.get(this.resizeEl, true);
40354     }else{
40355         this.resizeEl = this.el;
40356     }
40357     // handle view.xtype
40358     
40359  
40360     
40361     
40362     this.addEvents({
40363         /**
40364          * @event activate
40365          * Fires when this panel is activated. 
40366          * @param {Roo.ContentPanel} this
40367          */
40368         "activate" : true,
40369         /**
40370          * @event deactivate
40371          * Fires when this panel is activated. 
40372          * @param {Roo.ContentPanel} this
40373          */
40374         "deactivate" : true,
40375
40376         /**
40377          * @event resize
40378          * Fires when this panel is resized if fitToFrame is true.
40379          * @param {Roo.ContentPanel} this
40380          * @param {Number} width The width after any component adjustments
40381          * @param {Number} height The height after any component adjustments
40382          */
40383         "resize" : true,
40384         
40385          /**
40386          * @event render
40387          * Fires when this tab is created
40388          * @param {Roo.ContentPanel} this
40389          */
40390         "render" : true,
40391         
40392           /**
40393          * @event scroll
40394          * Fires when this content is scrolled
40395          * @param {Roo.ContentPanel} this
40396          * @param {Event} scrollEvent
40397          */
40398         "scroll" : true
40399         
40400         
40401         
40402     });
40403     
40404
40405     
40406     
40407     if(this.autoScroll && !this.iframe){
40408         this.resizeEl.setStyle("overflow", "auto");
40409         this.resizeEl.on('scroll', this.onScroll, this);
40410     } else {
40411         // fix randome scrolling
40412         //this.el.on('scroll', function() {
40413         //    Roo.log('fix random scolling');
40414         //    this.scrollTo('top',0); 
40415         //});
40416     }
40417     content = content || this.content;
40418     if(content){
40419         this.setContent(content);
40420     }
40421     if(config && config.url){
40422         this.setUrl(this.url, this.params, this.loadOnce);
40423     }
40424     
40425     
40426     
40427     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40428     
40429     if (this.view && typeof(this.view.xtype) != 'undefined') {
40430         this.view.el = this.el.appendChild(document.createElement("div"));
40431         this.view = Roo.factory(this.view); 
40432         this.view.render  &&  this.view.render(false, '');  
40433     }
40434     
40435     
40436     this.fireEvent('render', this);
40437 };
40438
40439 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40440     
40441     cls : '',
40442     background : '',
40443     
40444     tabTip : '',
40445     
40446     iframe : false,
40447     iframeEl : false,
40448     
40449     /* Resize Element - use this to work out scroll etc. */
40450     resizeEl : false,
40451     
40452     setRegion : function(region){
40453         this.region = region;
40454         this.setActiveClass(region && !this.background);
40455     },
40456     
40457     
40458     setActiveClass: function(state)
40459     {
40460         if(state){
40461            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40462            this.el.setStyle('position','relative');
40463         }else{
40464            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40465            this.el.setStyle('position', 'absolute');
40466         } 
40467     },
40468     
40469     /**
40470      * Returns the toolbar for this Panel if one was configured. 
40471      * @return {Roo.Toolbar} 
40472      */
40473     getToolbar : function(){
40474         return this.toolbar;
40475     },
40476     
40477     setActiveState : function(active)
40478     {
40479         this.active = active;
40480         this.setActiveClass(active);
40481         if(!active){
40482             if(this.fireEvent("deactivate", this) === false){
40483                 return false;
40484             }
40485             return true;
40486         }
40487         this.fireEvent("activate", this);
40488         return true;
40489     },
40490     /**
40491      * Updates this panel's element (not for iframe)
40492      * @param {String} content The new content
40493      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40494     */
40495     setContent : function(content, loadScripts){
40496         if (this.iframe) {
40497             return;
40498         }
40499         
40500         this.el.update(content, loadScripts);
40501     },
40502
40503     ignoreResize : function(w, h)
40504     {
40505         return false; // always resize?
40506         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40507             return true;
40508         }else{
40509             this.lastSize = {width: w, height: h};
40510             return false;
40511         }
40512     },
40513     /**
40514      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40515      * @return {Roo.UpdateManager} The UpdateManager
40516      */
40517     getUpdateManager : function(){
40518         if (this.iframe) {
40519             return false;
40520         }
40521         return this.el.getUpdateManager();
40522     },
40523      /**
40524      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40525      * Does not work with IFRAME contents
40526      * @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:
40527 <pre><code>
40528 panel.load({
40529     url: "your-url.php",
40530     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40531     callback: yourFunction,
40532     scope: yourObject, //(optional scope)
40533     discardUrl: false,
40534     nocache: false,
40535     text: "Loading...",
40536     timeout: 30,
40537     scripts: false
40538 });
40539 </code></pre>
40540      
40541      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40542      * 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.
40543      * @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}
40544      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40545      * @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.
40546      * @return {Roo.ContentPanel} this
40547      */
40548     load : function(){
40549         
40550         if (this.iframe) {
40551             return this;
40552         }
40553         
40554         var um = this.el.getUpdateManager();
40555         um.update.apply(um, arguments);
40556         return this;
40557     },
40558
40559
40560     /**
40561      * 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.
40562      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40563      * @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)
40564      * @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)
40565      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40566      */
40567     setUrl : function(url, params, loadOnce){
40568         if (this.iframe) {
40569             this.iframeEl.dom.src = url;
40570             return false;
40571         }
40572         
40573         if(this.refreshDelegate){
40574             this.removeListener("activate", this.refreshDelegate);
40575         }
40576         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40577         this.on("activate", this.refreshDelegate);
40578         return this.el.getUpdateManager();
40579     },
40580     
40581     _handleRefresh : function(url, params, loadOnce){
40582         if(!loadOnce || !this.loaded){
40583             var updater = this.el.getUpdateManager();
40584             updater.update(url, params, this._setLoaded.createDelegate(this));
40585         }
40586     },
40587     
40588     _setLoaded : function(){
40589         this.loaded = true;
40590     }, 
40591     
40592     /**
40593      * Returns this panel's id
40594      * @return {String} 
40595      */
40596     getId : function(){
40597         return this.el.id;
40598     },
40599     
40600     /** 
40601      * Returns this panel's element - used by regiosn to add.
40602      * @return {Roo.Element} 
40603      */
40604     getEl : function(){
40605         return this.wrapEl || this.el;
40606     },
40607     
40608    
40609     
40610     adjustForComponents : function(width, height)
40611     {
40612         //Roo.log('adjustForComponents ');
40613         if(this.resizeEl != this.el){
40614             width -= this.el.getFrameWidth('lr');
40615             height -= this.el.getFrameWidth('tb');
40616         }
40617         if(this.toolbar){
40618             var te = this.toolbar.getEl();
40619             te.setWidth(width);
40620             height -= te.getHeight();
40621         }
40622         if(this.footer){
40623             var te = this.footer.getEl();
40624             te.setWidth(width);
40625             height -= te.getHeight();
40626         }
40627         
40628         
40629         if(this.adjustments){
40630             width += this.adjustments[0];
40631             height += this.adjustments[1];
40632         }
40633         return {"width": width, "height": height};
40634     },
40635     
40636     setSize : function(width, height){
40637         if(this.fitToFrame && !this.ignoreResize(width, height)){
40638             if(this.fitContainer && this.resizeEl != this.el){
40639                 this.el.setSize(width, height);
40640             }
40641             var size = this.adjustForComponents(width, height);
40642             if (this.iframe) {
40643                 this.iframeEl.setSize(width,height);
40644             }
40645             
40646             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40647             this.fireEvent('resize', this, size.width, size.height);
40648             
40649             
40650         }
40651     },
40652     
40653     /**
40654      * Returns this panel's title
40655      * @return {String} 
40656      */
40657     getTitle : function(){
40658         
40659         if (typeof(this.title) != 'object') {
40660             return this.title;
40661         }
40662         
40663         var t = '';
40664         for (var k in this.title) {
40665             if (!this.title.hasOwnProperty(k)) {
40666                 continue;
40667             }
40668             
40669             if (k.indexOf('-') >= 0) {
40670                 var s = k.split('-');
40671                 for (var i = 0; i<s.length; i++) {
40672                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40673                 }
40674             } else {
40675                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40676             }
40677         }
40678         return t;
40679     },
40680     
40681     /**
40682      * Set this panel's title
40683      * @param {String} title
40684      */
40685     setTitle : function(title){
40686         this.title = title;
40687         if(this.region){
40688             this.region.updatePanelTitle(this, title);
40689         }
40690     },
40691     
40692     /**
40693      * Returns true is this panel was configured to be closable
40694      * @return {Boolean} 
40695      */
40696     isClosable : function(){
40697         return this.closable;
40698     },
40699     
40700     beforeSlide : function(){
40701         this.el.clip();
40702         this.resizeEl.clip();
40703     },
40704     
40705     afterSlide : function(){
40706         this.el.unclip();
40707         this.resizeEl.unclip();
40708     },
40709     
40710     /**
40711      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40712      *   Will fail silently if the {@link #setUrl} method has not been called.
40713      *   This does not activate the panel, just updates its content.
40714      */
40715     refresh : function(){
40716         if(this.refreshDelegate){
40717            this.loaded = false;
40718            this.refreshDelegate();
40719         }
40720     },
40721     
40722     /**
40723      * Destroys this panel
40724      */
40725     destroy : function(){
40726         this.el.removeAllListeners();
40727         var tempEl = document.createElement("span");
40728         tempEl.appendChild(this.el.dom);
40729         tempEl.innerHTML = "";
40730         this.el.remove();
40731         this.el = null;
40732     },
40733     
40734     /**
40735      * form - if the content panel contains a form - this is a reference to it.
40736      * @type {Roo.form.Form}
40737      */
40738     form : false,
40739     /**
40740      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40741      *    This contains a reference to it.
40742      * @type {Roo.View}
40743      */
40744     view : false,
40745     
40746       /**
40747      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40748      * <pre><code>
40749
40750 layout.addxtype({
40751        xtype : 'Form',
40752        items: [ .... ]
40753    }
40754 );
40755
40756 </code></pre>
40757      * @param {Object} cfg Xtype definition of item to add.
40758      */
40759     
40760     
40761     getChildContainer: function () {
40762         return this.getEl();
40763     },
40764     
40765     
40766     onScroll : function(e)
40767     {
40768         this.fireEvent('scroll', this, e);
40769     }
40770     
40771     
40772     /*
40773         var  ret = new Roo.factory(cfg);
40774         return ret;
40775         
40776         
40777         // add form..
40778         if (cfg.xtype.match(/^Form$/)) {
40779             
40780             var el;
40781             //if (this.footer) {
40782             //    el = this.footer.container.insertSibling(false, 'before');
40783             //} else {
40784                 el = this.el.createChild();
40785             //}
40786
40787             this.form = new  Roo.form.Form(cfg);
40788             
40789             
40790             if ( this.form.allItems.length) {
40791                 this.form.render(el.dom);
40792             }
40793             return this.form;
40794         }
40795         // should only have one of theses..
40796         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40797             // views.. should not be just added - used named prop 'view''
40798             
40799             cfg.el = this.el.appendChild(document.createElement("div"));
40800             // factory?
40801             
40802             var ret = new Roo.factory(cfg);
40803              
40804              ret.render && ret.render(false, ''); // render blank..
40805             this.view = ret;
40806             return ret;
40807         }
40808         return false;
40809     }
40810     \*/
40811 });
40812  
40813 /**
40814  * @class Roo.bootstrap.panel.Grid
40815  * @extends Roo.bootstrap.panel.Content
40816  * @constructor
40817  * Create a new GridPanel.
40818  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40819  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40820  * @param {Object} config A the config object
40821   
40822  */
40823
40824
40825
40826 Roo.bootstrap.panel.Grid = function(config)
40827 {
40828     
40829       
40830     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40831         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40832
40833     config.el = this.wrapper;
40834     //this.el = this.wrapper;
40835     
40836       if (config.container) {
40837         // ctor'ed from a Border/panel.grid
40838         
40839         
40840         this.wrapper.setStyle("overflow", "hidden");
40841         this.wrapper.addClass('roo-grid-container');
40842
40843     }
40844     
40845     
40846     if(config.toolbar){
40847         var tool_el = this.wrapper.createChild();    
40848         this.toolbar = Roo.factory(config.toolbar);
40849         var ti = [];
40850         if (config.toolbar.items) {
40851             ti = config.toolbar.items ;
40852             delete config.toolbar.items ;
40853         }
40854         
40855         var nitems = [];
40856         this.toolbar.render(tool_el);
40857         for(var i =0;i < ti.length;i++) {
40858           //  Roo.log(['add child', items[i]]);
40859             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40860         }
40861         this.toolbar.items = nitems;
40862         
40863         delete config.toolbar;
40864     }
40865     
40866     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40867     config.grid.scrollBody = true;;
40868     config.grid.monitorWindowResize = false; // turn off autosizing
40869     config.grid.autoHeight = false;
40870     config.grid.autoWidth = false;
40871     
40872     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40873     
40874     if (config.background) {
40875         // render grid on panel activation (if panel background)
40876         this.on('activate', function(gp) {
40877             if (!gp.grid.rendered) {
40878                 gp.grid.render(this.wrapper);
40879                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40880             }
40881         });
40882             
40883     } else {
40884         this.grid.render(this.wrapper);
40885         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40886
40887     }
40888     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40889     // ??? needed ??? config.el = this.wrapper;
40890     
40891     
40892     
40893   
40894     // xtype created footer. - not sure if will work as we normally have to render first..
40895     if (this.footer && !this.footer.el && this.footer.xtype) {
40896         
40897         var ctr = this.grid.getView().getFooterPanel(true);
40898         this.footer.dataSource = this.grid.dataSource;
40899         this.footer = Roo.factory(this.footer, Roo);
40900         this.footer.render(ctr);
40901         
40902     }
40903     
40904     
40905     
40906     
40907      
40908 };
40909
40910 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40911 {
40912     // private
40913     is_resizing : false,
40914     
40915     getId : function(){
40916         return this.grid.id;
40917     },
40918     
40919     /**
40920      * Returns the grid for this panel
40921      * @return {Roo.bootstrap.Table} 
40922      */
40923     getGrid : function(){
40924         return this.grid;    
40925     },
40926     
40927     setSize : function(width, height)
40928     {
40929         if (this.is_resizing) {
40930             return;
40931         
40932         }
40933         this.is_resizing = true;
40934         if(!this.ignoreResize(width, height)){
40935             var grid = this.grid;
40936             var size = this.adjustForComponents(width, height);
40937             // tfoot is not a footer?
40938           
40939             
40940             var gridel = grid.getGridEl();
40941             gridel.setSize(size.width, size.height);
40942             
40943             var tbd = grid.getGridEl().select('tbody', true).first();
40944             var thd = grid.getGridEl().select('thead',true).first();
40945             var tbf= grid.getGridEl().select('tfoot', true).first();
40946
40947             if (tbf) {
40948                 size.height -= tbf.getHeight();
40949             }
40950             if (thd) {
40951                 size.height -= thd.getHeight();
40952             }
40953             
40954             tbd.setSize(size.width, size.height );
40955             // this is for the account management tab -seems to work there.
40956             var thd = grid.getGridEl().select('thead',true).first();
40957             //if (tbd) {
40958             //    tbd.setSize(size.width, size.height - thd.getHeight());
40959             //}
40960              
40961             grid.autoSize();
40962         }
40963         this.is_resizing = false;
40964     },
40965      
40966     
40967     
40968     beforeSlide : function(){
40969         this.grid.getView().scroller.clip();
40970     },
40971     
40972     afterSlide : function(){
40973         this.grid.getView().scroller.unclip();
40974     },
40975     
40976     destroy : function(){
40977         this.grid.destroy();
40978         delete this.grid;
40979         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40980     }
40981 });
40982
40983 /**
40984  * @class Roo.bootstrap.panel.Nest
40985  * @extends Roo.bootstrap.panel.Content
40986  * @constructor
40987  * Create a new Panel, that can contain a layout.Border.
40988  * 
40989  * 
40990  * @param {String/Object} config A string to set only the title or a config object
40991  */
40992 Roo.bootstrap.panel.Nest = function(config)
40993 {
40994     // construct with only one argument..
40995     /* FIXME - implement nicer consturctors
40996     if (layout.layout) {
40997         config = layout;
40998         layout = config.layout;
40999         delete config.layout;
41000     }
41001     if (layout.xtype && !layout.getEl) {
41002         // then layout needs constructing..
41003         layout = Roo.factory(layout, Roo);
41004     }
41005     */
41006     
41007     config.el =  config.layout.getEl();
41008     
41009     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41010     
41011     config.layout.monitorWindowResize = false; // turn off autosizing
41012     this.layout = config.layout;
41013     this.layout.getEl().addClass("roo-layout-nested-layout");
41014     this.layout.parent = this;
41015     
41016     
41017     
41018     
41019 };
41020
41021 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41022     /**
41023     * @cfg {Roo.BorderLayout} layout The layout for this panel
41024     */
41025     layout : false,
41026
41027     setSize : function(width, height){
41028         if(!this.ignoreResize(width, height)){
41029             var size = this.adjustForComponents(width, height);
41030             var el = this.layout.getEl();
41031             if (size.height < 1) {
41032                 el.setWidth(size.width);   
41033             } else {
41034                 el.setSize(size.width, size.height);
41035             }
41036             var touch = el.dom.offsetWidth;
41037             this.layout.layout();
41038             // ie requires a double layout on the first pass
41039             if(Roo.isIE && !this.initialized){
41040                 this.initialized = true;
41041                 this.layout.layout();
41042             }
41043         }
41044     },
41045     
41046     // activate all subpanels if not currently active..
41047     
41048     setActiveState : function(active){
41049         this.active = active;
41050         this.setActiveClass(active);
41051         
41052         if(!active){
41053             this.fireEvent("deactivate", this);
41054             return;
41055         }
41056         
41057         this.fireEvent("activate", this);
41058         // not sure if this should happen before or after..
41059         if (!this.layout) {
41060             return; // should not happen..
41061         }
41062         var reg = false;
41063         for (var r in this.layout.regions) {
41064             reg = this.layout.getRegion(r);
41065             if (reg.getActivePanel()) {
41066                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41067                 reg.setActivePanel(reg.getActivePanel());
41068                 continue;
41069             }
41070             if (!reg.panels.length) {
41071                 continue;
41072             }
41073             reg.showPanel(reg.getPanel(0));
41074         }
41075         
41076         
41077         
41078         
41079     },
41080     
41081     /**
41082      * Returns the nested BorderLayout for this panel
41083      * @return {Roo.BorderLayout} 
41084      */
41085     getLayout : function(){
41086         return this.layout;
41087     },
41088     
41089      /**
41090      * Adds a xtype elements to the layout of the nested panel
41091      * <pre><code>
41092
41093 panel.addxtype({
41094        xtype : 'ContentPanel',
41095        region: 'west',
41096        items: [ .... ]
41097    }
41098 );
41099
41100 panel.addxtype({
41101         xtype : 'NestedLayoutPanel',
41102         region: 'west',
41103         layout: {
41104            center: { },
41105            west: { }   
41106         },
41107         items : [ ... list of content panels or nested layout panels.. ]
41108    }
41109 );
41110 </code></pre>
41111      * @param {Object} cfg Xtype definition of item to add.
41112      */
41113     addxtype : function(cfg) {
41114         return this.layout.addxtype(cfg);
41115     
41116     }
41117 });/*
41118  * Based on:
41119  * Ext JS Library 1.1.1
41120  * Copyright(c) 2006-2007, Ext JS, LLC.
41121  *
41122  * Originally Released Under LGPL - original licence link has changed is not relivant.
41123  *
41124  * Fork - LGPL
41125  * <script type="text/javascript">
41126  */
41127 /**
41128  * @class Roo.TabPanel
41129  * @extends Roo.util.Observable
41130  * A lightweight tab container.
41131  * <br><br>
41132  * Usage:
41133  * <pre><code>
41134 // basic tabs 1, built from existing content
41135 var tabs = new Roo.TabPanel("tabs1");
41136 tabs.addTab("script", "View Script");
41137 tabs.addTab("markup", "View Markup");
41138 tabs.activate("script");
41139
41140 // more advanced tabs, built from javascript
41141 var jtabs = new Roo.TabPanel("jtabs");
41142 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41143
41144 // set up the UpdateManager
41145 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41146 var updater = tab2.getUpdateManager();
41147 updater.setDefaultUrl("ajax1.htm");
41148 tab2.on('activate', updater.refresh, updater, true);
41149
41150 // Use setUrl for Ajax loading
41151 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41152 tab3.setUrl("ajax2.htm", null, true);
41153
41154 // Disabled tab
41155 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41156 tab4.disable();
41157
41158 jtabs.activate("jtabs-1");
41159  * </code></pre>
41160  * @constructor
41161  * Create a new TabPanel.
41162  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41163  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41164  */
41165 Roo.bootstrap.panel.Tabs = function(config){
41166     /**
41167     * The container element for this TabPanel.
41168     * @type Roo.Element
41169     */
41170     this.el = Roo.get(config.el);
41171     delete config.el;
41172     if(config){
41173         if(typeof config == "boolean"){
41174             this.tabPosition = config ? "bottom" : "top";
41175         }else{
41176             Roo.apply(this, config);
41177         }
41178     }
41179     
41180     if(this.tabPosition == "bottom"){
41181         // if tabs are at the bottom = create the body first.
41182         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41183         this.el.addClass("roo-tabs-bottom");
41184     }
41185     // next create the tabs holders
41186     
41187     if (this.tabPosition == "west"){
41188         
41189         var reg = this.region; // fake it..
41190         while (reg) {
41191             if (!reg.mgr.parent) {
41192                 break;
41193             }
41194             reg = reg.mgr.parent.region;
41195         }
41196         Roo.log("got nest?");
41197         Roo.log(reg);
41198         if (reg.mgr.getRegion('west')) {
41199             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41200             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41201             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41202             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41203             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41204         
41205             
41206         }
41207         
41208         
41209     } else {
41210      
41211         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41212         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41213         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41214         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41215     }
41216     
41217     
41218     if(Roo.isIE){
41219         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41220     }
41221     
41222     // finally - if tabs are at the top, then create the body last..
41223     if(this.tabPosition != "bottom"){
41224         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41225          * @type Roo.Element
41226          */
41227         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41228         this.el.addClass("roo-tabs-top");
41229     }
41230     this.items = [];
41231
41232     this.bodyEl.setStyle("position", "relative");
41233
41234     this.active = null;
41235     this.activateDelegate = this.activate.createDelegate(this);
41236
41237     this.addEvents({
41238         /**
41239          * @event tabchange
41240          * Fires when the active tab changes
41241          * @param {Roo.TabPanel} this
41242          * @param {Roo.TabPanelItem} activePanel The new active tab
41243          */
41244         "tabchange": true,
41245         /**
41246          * @event beforetabchange
41247          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41248          * @param {Roo.TabPanel} this
41249          * @param {Object} e Set cancel to true on this object to cancel the tab change
41250          * @param {Roo.TabPanelItem} tab The tab being changed to
41251          */
41252         "beforetabchange" : true
41253     });
41254
41255     Roo.EventManager.onWindowResize(this.onResize, this);
41256     this.cpad = this.el.getPadding("lr");
41257     this.hiddenCount = 0;
41258
41259
41260     // toolbar on the tabbar support...
41261     if (this.toolbar) {
41262         alert("no toolbar support yet");
41263         this.toolbar  = false;
41264         /*
41265         var tcfg = this.toolbar;
41266         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41267         this.toolbar = new Roo.Toolbar(tcfg);
41268         if (Roo.isSafari) {
41269             var tbl = tcfg.container.child('table', true);
41270             tbl.setAttribute('width', '100%');
41271         }
41272         */
41273         
41274     }
41275    
41276
41277
41278     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41279 };
41280
41281 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41282     /*
41283      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41284      */
41285     tabPosition : "top",
41286     /*
41287      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41288      */
41289     currentTabWidth : 0,
41290     /*
41291      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41292      */
41293     minTabWidth : 40,
41294     /*
41295      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41296      */
41297     maxTabWidth : 250,
41298     /*
41299      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41300      */
41301     preferredTabWidth : 175,
41302     /*
41303      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41304      */
41305     resizeTabs : false,
41306     /*
41307      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41308      */
41309     monitorResize : true,
41310     /*
41311      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41312      */
41313     toolbar : false,  // set by caller..
41314     
41315     region : false, /// set by caller
41316     
41317     disableTooltips : true, // not used yet...
41318
41319     /**
41320      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41321      * @param {String} id The id of the div to use <b>or create</b>
41322      * @param {String} text The text for the tab
41323      * @param {String} content (optional) Content to put in the TabPanelItem body
41324      * @param {Boolean} closable (optional) True to create a close icon on the tab
41325      * @return {Roo.TabPanelItem} The created TabPanelItem
41326      */
41327     addTab : function(id, text, content, closable, tpl)
41328     {
41329         var item = new Roo.bootstrap.panel.TabItem({
41330             panel: this,
41331             id : id,
41332             text : text,
41333             closable : closable,
41334             tpl : tpl
41335         });
41336         this.addTabItem(item);
41337         if(content){
41338             item.setContent(content);
41339         }
41340         return item;
41341     },
41342
41343     /**
41344      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41345      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41346      * @return {Roo.TabPanelItem}
41347      */
41348     getTab : function(id){
41349         return this.items[id];
41350     },
41351
41352     /**
41353      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41354      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41355      */
41356     hideTab : function(id){
41357         var t = this.items[id];
41358         if(!t.isHidden()){
41359            t.setHidden(true);
41360            this.hiddenCount++;
41361            this.autoSizeTabs();
41362         }
41363     },
41364
41365     /**
41366      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41367      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41368      */
41369     unhideTab : function(id){
41370         var t = this.items[id];
41371         if(t.isHidden()){
41372            t.setHidden(false);
41373            this.hiddenCount--;
41374            this.autoSizeTabs();
41375         }
41376     },
41377
41378     /**
41379      * Adds an existing {@link Roo.TabPanelItem}.
41380      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41381      */
41382     addTabItem : function(item)
41383     {
41384         this.items[item.id] = item;
41385         this.items.push(item);
41386         this.autoSizeTabs();
41387       //  if(this.resizeTabs){
41388     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41389   //         this.autoSizeTabs();
41390 //        }else{
41391 //            item.autoSize();
41392        // }
41393     },
41394
41395     /**
41396      * Removes a {@link Roo.TabPanelItem}.
41397      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41398      */
41399     removeTab : function(id){
41400         var items = this.items;
41401         var tab = items[id];
41402         if(!tab) { return; }
41403         var index = items.indexOf(tab);
41404         if(this.active == tab && items.length > 1){
41405             var newTab = this.getNextAvailable(index);
41406             if(newTab) {
41407                 newTab.activate();
41408             }
41409         }
41410         this.stripEl.dom.removeChild(tab.pnode.dom);
41411         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41412             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41413         }
41414         items.splice(index, 1);
41415         delete this.items[tab.id];
41416         tab.fireEvent("close", tab);
41417         tab.purgeListeners();
41418         this.autoSizeTabs();
41419     },
41420
41421     getNextAvailable : function(start){
41422         var items = this.items;
41423         var index = start;
41424         // look for a next tab that will slide over to
41425         // replace the one being removed
41426         while(index < items.length){
41427             var item = items[++index];
41428             if(item && !item.isHidden()){
41429                 return item;
41430             }
41431         }
41432         // if one isn't found select the previous tab (on the left)
41433         index = start;
41434         while(index >= 0){
41435             var item = items[--index];
41436             if(item && !item.isHidden()){
41437                 return item;
41438             }
41439         }
41440         return null;
41441     },
41442
41443     /**
41444      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41445      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41446      */
41447     disableTab : function(id){
41448         var tab = this.items[id];
41449         if(tab && this.active != tab){
41450             tab.disable();
41451         }
41452     },
41453
41454     /**
41455      * Enables a {@link Roo.TabPanelItem} that is disabled.
41456      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41457      */
41458     enableTab : function(id){
41459         var tab = this.items[id];
41460         tab.enable();
41461     },
41462
41463     /**
41464      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41465      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41466      * @return {Roo.TabPanelItem} The TabPanelItem.
41467      */
41468     activate : function(id)
41469     {
41470         //Roo.log('activite:'  + id);
41471         
41472         var tab = this.items[id];
41473         if(!tab){
41474             return null;
41475         }
41476         if(tab == this.active || tab.disabled){
41477             return tab;
41478         }
41479         var e = {};
41480         this.fireEvent("beforetabchange", this, e, tab);
41481         if(e.cancel !== true && !tab.disabled){
41482             if(this.active){
41483                 this.active.hide();
41484             }
41485             this.active = this.items[id];
41486             this.active.show();
41487             this.fireEvent("tabchange", this, this.active);
41488         }
41489         return tab;
41490     },
41491
41492     /**
41493      * Gets the active {@link Roo.TabPanelItem}.
41494      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41495      */
41496     getActiveTab : function(){
41497         return this.active;
41498     },
41499
41500     /**
41501      * Updates the tab body element to fit the height of the container element
41502      * for overflow scrolling
41503      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41504      */
41505     syncHeight : function(targetHeight){
41506         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41507         var bm = this.bodyEl.getMargins();
41508         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41509         this.bodyEl.setHeight(newHeight);
41510         return newHeight;
41511     },
41512
41513     onResize : function(){
41514         if(this.monitorResize){
41515             this.autoSizeTabs();
41516         }
41517     },
41518
41519     /**
41520      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41521      */
41522     beginUpdate : function(){
41523         this.updating = true;
41524     },
41525
41526     /**
41527      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41528      */
41529     endUpdate : function(){
41530         this.updating = false;
41531         this.autoSizeTabs();
41532     },
41533
41534     /**
41535      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41536      */
41537     autoSizeTabs : function()
41538     {
41539         var count = this.items.length;
41540         var vcount = count - this.hiddenCount;
41541         
41542         if (vcount < 2) {
41543             this.stripEl.hide();
41544         } else {
41545             this.stripEl.show();
41546         }
41547         
41548         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41549             return;
41550         }
41551         
41552         
41553         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41554         var availWidth = Math.floor(w / vcount);
41555         var b = this.stripBody;
41556         if(b.getWidth() > w){
41557             var tabs = this.items;
41558             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41559             if(availWidth < this.minTabWidth){
41560                 /*if(!this.sleft){    // incomplete scrolling code
41561                     this.createScrollButtons();
41562                 }
41563                 this.showScroll();
41564                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41565             }
41566         }else{
41567             if(this.currentTabWidth < this.preferredTabWidth){
41568                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41569             }
41570         }
41571     },
41572
41573     /**
41574      * Returns the number of tabs in this TabPanel.
41575      * @return {Number}
41576      */
41577      getCount : function(){
41578          return this.items.length;
41579      },
41580
41581     /**
41582      * Resizes all the tabs to the passed width
41583      * @param {Number} The new width
41584      */
41585     setTabWidth : function(width){
41586         this.currentTabWidth = width;
41587         for(var i = 0, len = this.items.length; i < len; i++) {
41588                 if(!this.items[i].isHidden()) {
41589                 this.items[i].setWidth(width);
41590             }
41591         }
41592     },
41593
41594     /**
41595      * Destroys this TabPanel
41596      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41597      */
41598     destroy : function(removeEl){
41599         Roo.EventManager.removeResizeListener(this.onResize, this);
41600         for(var i = 0, len = this.items.length; i < len; i++){
41601             this.items[i].purgeListeners();
41602         }
41603         if(removeEl === true){
41604             this.el.update("");
41605             this.el.remove();
41606         }
41607     },
41608     
41609     createStrip : function(container)
41610     {
41611         var strip = document.createElement("nav");
41612         strip.className = Roo.bootstrap.version == 4 ?
41613             "navbar-light bg-light" : 
41614             "navbar navbar-default"; //"x-tabs-wrap";
41615         container.appendChild(strip);
41616         return strip;
41617     },
41618     
41619     createStripList : function(strip)
41620     {
41621         // div wrapper for retard IE
41622         // returns the "tr" element.
41623         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41624         //'<div class="x-tabs-strip-wrap">'+
41625           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41626           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41627         return strip.firstChild; //.firstChild.firstChild.firstChild;
41628     },
41629     createBody : function(container)
41630     {
41631         var body = document.createElement("div");
41632         Roo.id(body, "tab-body");
41633         //Roo.fly(body).addClass("x-tabs-body");
41634         Roo.fly(body).addClass("tab-content");
41635         container.appendChild(body);
41636         return body;
41637     },
41638     createItemBody :function(bodyEl, id){
41639         var body = Roo.getDom(id);
41640         if(!body){
41641             body = document.createElement("div");
41642             body.id = id;
41643         }
41644         //Roo.fly(body).addClass("x-tabs-item-body");
41645         Roo.fly(body).addClass("tab-pane");
41646          bodyEl.insertBefore(body, bodyEl.firstChild);
41647         return body;
41648     },
41649     /** @private */
41650     createStripElements :  function(stripEl, text, closable, tpl)
41651     {
41652         var td = document.createElement("li"); // was td..
41653         td.className = 'nav-item';
41654         
41655         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41656         
41657         
41658         stripEl.appendChild(td);
41659         /*if(closable){
41660             td.className = "x-tabs-closable";
41661             if(!this.closeTpl){
41662                 this.closeTpl = new Roo.Template(
41663                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41664                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41665                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41666                 );
41667             }
41668             var el = this.closeTpl.overwrite(td, {"text": text});
41669             var close = el.getElementsByTagName("div")[0];
41670             var inner = el.getElementsByTagName("em")[0];
41671             return {"el": el, "close": close, "inner": inner};
41672         } else {
41673         */
41674         // not sure what this is..
41675 //            if(!this.tabTpl){
41676                 //this.tabTpl = new Roo.Template(
41677                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41678                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41679                 //);
41680 //                this.tabTpl = new Roo.Template(
41681 //                   '<a href="#">' +
41682 //                   '<span unselectable="on"' +
41683 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41684 //                            ' >{text}</span></a>'
41685 //                );
41686 //                
41687 //            }
41688
41689
41690             var template = tpl || this.tabTpl || false;
41691             
41692             if(!template){
41693                 template =  new Roo.Template(
41694                         Roo.bootstrap.version == 4 ? 
41695                             (
41696                                 '<a class="nav-link" href="#" unselectable="on"' +
41697                                      (this.disableTooltips ? '' : ' title="{text}"') +
41698                                      ' >{text}</a>'
41699                             ) : (
41700                                 '<a class="nav-link" href="#">' +
41701                                 '<span unselectable="on"' +
41702                                          (this.disableTooltips ? '' : ' title="{text}"') +
41703                                     ' >{text}</span></a>'
41704                             )
41705                 );
41706             }
41707             
41708             switch (typeof(template)) {
41709                 case 'object' :
41710                     break;
41711                 case 'string' :
41712                     template = new Roo.Template(template);
41713                     break;
41714                 default :
41715                     break;
41716             }
41717             
41718             var el = template.overwrite(td, {"text": text});
41719             
41720             var inner = el.getElementsByTagName("span")[0];
41721             
41722             return {"el": el, "inner": inner};
41723             
41724     }
41725         
41726     
41727 });
41728
41729 /**
41730  * @class Roo.TabPanelItem
41731  * @extends Roo.util.Observable
41732  * Represents an individual item (tab plus body) in a TabPanel.
41733  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41734  * @param {String} id The id of this TabPanelItem
41735  * @param {String} text The text for the tab of this TabPanelItem
41736  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41737  */
41738 Roo.bootstrap.panel.TabItem = function(config){
41739     /**
41740      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41741      * @type Roo.TabPanel
41742      */
41743     this.tabPanel = config.panel;
41744     /**
41745      * The id for this TabPanelItem
41746      * @type String
41747      */
41748     this.id = config.id;
41749     /** @private */
41750     this.disabled = false;
41751     /** @private */
41752     this.text = config.text;
41753     /** @private */
41754     this.loaded = false;
41755     this.closable = config.closable;
41756
41757     /**
41758      * The body element for this TabPanelItem.
41759      * @type Roo.Element
41760      */
41761     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41762     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41763     this.bodyEl.setStyle("display", "block");
41764     this.bodyEl.setStyle("zoom", "1");
41765     //this.hideAction();
41766
41767     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41768     /** @private */
41769     this.el = Roo.get(els.el);
41770     this.inner = Roo.get(els.inner, true);
41771      this.textEl = Roo.bootstrap.version == 4 ?
41772         this.el : Roo.get(this.el.dom.firstChild, true);
41773
41774     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41775     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41776
41777     
41778 //    this.el.on("mousedown", this.onTabMouseDown, this);
41779     this.el.on("click", this.onTabClick, this);
41780     /** @private */
41781     if(config.closable){
41782         var c = Roo.get(els.close, true);
41783         c.dom.title = this.closeText;
41784         c.addClassOnOver("close-over");
41785         c.on("click", this.closeClick, this);
41786      }
41787
41788     this.addEvents({
41789          /**
41790          * @event activate
41791          * Fires when this tab becomes the active tab.
41792          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41793          * @param {Roo.TabPanelItem} this
41794          */
41795         "activate": true,
41796         /**
41797          * @event beforeclose
41798          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41799          * @param {Roo.TabPanelItem} this
41800          * @param {Object} e Set cancel to true on this object to cancel the close.
41801          */
41802         "beforeclose": true,
41803         /**
41804          * @event close
41805          * Fires when this tab is closed.
41806          * @param {Roo.TabPanelItem} this
41807          */
41808          "close": true,
41809         /**
41810          * @event deactivate
41811          * Fires when this tab is no longer the active tab.
41812          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41813          * @param {Roo.TabPanelItem} this
41814          */
41815          "deactivate" : true
41816     });
41817     this.hidden = false;
41818
41819     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41820 };
41821
41822 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41823            {
41824     purgeListeners : function(){
41825        Roo.util.Observable.prototype.purgeListeners.call(this);
41826        this.el.removeAllListeners();
41827     },
41828     /**
41829      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41830      */
41831     show : function(){
41832         this.status_node.addClass("active");
41833         this.showAction();
41834         if(Roo.isOpera){
41835             this.tabPanel.stripWrap.repaint();
41836         }
41837         this.fireEvent("activate", this.tabPanel, this);
41838     },
41839
41840     /**
41841      * Returns true if this tab is the active tab.
41842      * @return {Boolean}
41843      */
41844     isActive : function(){
41845         return this.tabPanel.getActiveTab() == this;
41846     },
41847
41848     /**
41849      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41850      */
41851     hide : function(){
41852         this.status_node.removeClass("active");
41853         this.hideAction();
41854         this.fireEvent("deactivate", this.tabPanel, this);
41855     },
41856
41857     hideAction : function(){
41858         this.bodyEl.hide();
41859         this.bodyEl.setStyle("position", "absolute");
41860         this.bodyEl.setLeft("-20000px");
41861         this.bodyEl.setTop("-20000px");
41862     },
41863
41864     showAction : function(){
41865         this.bodyEl.setStyle("position", "relative");
41866         this.bodyEl.setTop("");
41867         this.bodyEl.setLeft("");
41868         this.bodyEl.show();
41869     },
41870
41871     /**
41872      * Set the tooltip for the tab.
41873      * @param {String} tooltip The tab's tooltip
41874      */
41875     setTooltip : function(text){
41876         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41877             this.textEl.dom.qtip = text;
41878             this.textEl.dom.removeAttribute('title');
41879         }else{
41880             this.textEl.dom.title = text;
41881         }
41882     },
41883
41884     onTabClick : function(e){
41885         e.preventDefault();
41886         this.tabPanel.activate(this.id);
41887     },
41888
41889     onTabMouseDown : function(e){
41890         e.preventDefault();
41891         this.tabPanel.activate(this.id);
41892     },
41893 /*
41894     getWidth : function(){
41895         return this.inner.getWidth();
41896     },
41897
41898     setWidth : function(width){
41899         var iwidth = width - this.linode.getPadding("lr");
41900         this.inner.setWidth(iwidth);
41901         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41902         this.linode.setWidth(width);
41903     },
41904 */
41905     /**
41906      * Show or hide the tab
41907      * @param {Boolean} hidden True to hide or false to show.
41908      */
41909     setHidden : function(hidden){
41910         this.hidden = hidden;
41911         this.linode.setStyle("display", hidden ? "none" : "");
41912     },
41913
41914     /**
41915      * Returns true if this tab is "hidden"
41916      * @return {Boolean}
41917      */
41918     isHidden : function(){
41919         return this.hidden;
41920     },
41921
41922     /**
41923      * Returns the text for this tab
41924      * @return {String}
41925      */
41926     getText : function(){
41927         return this.text;
41928     },
41929     /*
41930     autoSize : function(){
41931         //this.el.beginMeasure();
41932         this.textEl.setWidth(1);
41933         /*
41934          *  #2804 [new] Tabs in Roojs
41935          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41936          */
41937         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41938         //this.el.endMeasure();
41939     //},
41940
41941     /**
41942      * Sets the text for the tab (Note: this also sets the tooltip text)
41943      * @param {String} text The tab's text and tooltip
41944      */
41945     setText : function(text){
41946         this.text = text;
41947         this.textEl.update(text);
41948         this.setTooltip(text);
41949         //if(!this.tabPanel.resizeTabs){
41950         //    this.autoSize();
41951         //}
41952     },
41953     /**
41954      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41955      */
41956     activate : function(){
41957         this.tabPanel.activate(this.id);
41958     },
41959
41960     /**
41961      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41962      */
41963     disable : function(){
41964         if(this.tabPanel.active != this){
41965             this.disabled = true;
41966             this.status_node.addClass("disabled");
41967         }
41968     },
41969
41970     /**
41971      * Enables this TabPanelItem if it was previously disabled.
41972      */
41973     enable : function(){
41974         this.disabled = false;
41975         this.status_node.removeClass("disabled");
41976     },
41977
41978     /**
41979      * Sets the content for this TabPanelItem.
41980      * @param {String} content The content
41981      * @param {Boolean} loadScripts true to look for and load scripts
41982      */
41983     setContent : function(content, loadScripts){
41984         this.bodyEl.update(content, loadScripts);
41985     },
41986
41987     /**
41988      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41989      * @return {Roo.UpdateManager} The UpdateManager
41990      */
41991     getUpdateManager : function(){
41992         return this.bodyEl.getUpdateManager();
41993     },
41994
41995     /**
41996      * Set a URL to be used to load the content for this TabPanelItem.
41997      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41998      * @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)
41999      * @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)
42000      * @return {Roo.UpdateManager} The UpdateManager
42001      */
42002     setUrl : function(url, params, loadOnce){
42003         if(this.refreshDelegate){
42004             this.un('activate', this.refreshDelegate);
42005         }
42006         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42007         this.on("activate", this.refreshDelegate);
42008         return this.bodyEl.getUpdateManager();
42009     },
42010
42011     /** @private */
42012     _handleRefresh : function(url, params, loadOnce){
42013         if(!loadOnce || !this.loaded){
42014             var updater = this.bodyEl.getUpdateManager();
42015             updater.update(url, params, this._setLoaded.createDelegate(this));
42016         }
42017     },
42018
42019     /**
42020      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42021      *   Will fail silently if the setUrl method has not been called.
42022      *   This does not activate the panel, just updates its content.
42023      */
42024     refresh : function(){
42025         if(this.refreshDelegate){
42026            this.loaded = false;
42027            this.refreshDelegate();
42028         }
42029     },
42030
42031     /** @private */
42032     _setLoaded : function(){
42033         this.loaded = true;
42034     },
42035
42036     /** @private */
42037     closeClick : function(e){
42038         var o = {};
42039         e.stopEvent();
42040         this.fireEvent("beforeclose", this, o);
42041         if(o.cancel !== true){
42042             this.tabPanel.removeTab(this.id);
42043         }
42044     },
42045     /**
42046      * The text displayed in the tooltip for the close icon.
42047      * @type String
42048      */
42049     closeText : "Close this tab"
42050 });
42051 /**
42052 *    This script refer to:
42053 *    Title: International Telephone Input
42054 *    Author: Jack O'Connor
42055 *    Code version:  v12.1.12
42056 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42057 **/
42058
42059 Roo.bootstrap.form.PhoneInputData = function() {
42060     var d = [
42061       [
42062         "Afghanistan (‫افغانستان‬‎)",
42063         "af",
42064         "93"
42065       ],
42066       [
42067         "Albania (Shqipëri)",
42068         "al",
42069         "355"
42070       ],
42071       [
42072         "Algeria (‫الجزائر‬‎)",
42073         "dz",
42074         "213"
42075       ],
42076       [
42077         "American Samoa",
42078         "as",
42079         "1684"
42080       ],
42081       [
42082         "Andorra",
42083         "ad",
42084         "376"
42085       ],
42086       [
42087         "Angola",
42088         "ao",
42089         "244"
42090       ],
42091       [
42092         "Anguilla",
42093         "ai",
42094         "1264"
42095       ],
42096       [
42097         "Antigua and Barbuda",
42098         "ag",
42099         "1268"
42100       ],
42101       [
42102         "Argentina",
42103         "ar",
42104         "54"
42105       ],
42106       [
42107         "Armenia (Հայաստան)",
42108         "am",
42109         "374"
42110       ],
42111       [
42112         "Aruba",
42113         "aw",
42114         "297"
42115       ],
42116       [
42117         "Australia",
42118         "au",
42119         "61",
42120         0
42121       ],
42122       [
42123         "Austria (Österreich)",
42124         "at",
42125         "43"
42126       ],
42127       [
42128         "Azerbaijan (Azərbaycan)",
42129         "az",
42130         "994"
42131       ],
42132       [
42133         "Bahamas",
42134         "bs",
42135         "1242"
42136       ],
42137       [
42138         "Bahrain (‫البحرين‬‎)",
42139         "bh",
42140         "973"
42141       ],
42142       [
42143         "Bangladesh (বাংলাদেশ)",
42144         "bd",
42145         "880"
42146       ],
42147       [
42148         "Barbados",
42149         "bb",
42150         "1246"
42151       ],
42152       [
42153         "Belarus (Беларусь)",
42154         "by",
42155         "375"
42156       ],
42157       [
42158         "Belgium (België)",
42159         "be",
42160         "32"
42161       ],
42162       [
42163         "Belize",
42164         "bz",
42165         "501"
42166       ],
42167       [
42168         "Benin (Bénin)",
42169         "bj",
42170         "229"
42171       ],
42172       [
42173         "Bermuda",
42174         "bm",
42175         "1441"
42176       ],
42177       [
42178         "Bhutan (འབྲུག)",
42179         "bt",
42180         "975"
42181       ],
42182       [
42183         "Bolivia",
42184         "bo",
42185         "591"
42186       ],
42187       [
42188         "Bosnia and Herzegovina (Босна и Херцеговина)",
42189         "ba",
42190         "387"
42191       ],
42192       [
42193         "Botswana",
42194         "bw",
42195         "267"
42196       ],
42197       [
42198         "Brazil (Brasil)",
42199         "br",
42200         "55"
42201       ],
42202       [
42203         "British Indian Ocean Territory",
42204         "io",
42205         "246"
42206       ],
42207       [
42208         "British Virgin Islands",
42209         "vg",
42210         "1284"
42211       ],
42212       [
42213         "Brunei",
42214         "bn",
42215         "673"
42216       ],
42217       [
42218         "Bulgaria (България)",
42219         "bg",
42220         "359"
42221       ],
42222       [
42223         "Burkina Faso",
42224         "bf",
42225         "226"
42226       ],
42227       [
42228         "Burundi (Uburundi)",
42229         "bi",
42230         "257"
42231       ],
42232       [
42233         "Cambodia (កម្ពុជា)",
42234         "kh",
42235         "855"
42236       ],
42237       [
42238         "Cameroon (Cameroun)",
42239         "cm",
42240         "237"
42241       ],
42242       [
42243         "Canada",
42244         "ca",
42245         "1",
42246         1,
42247         ["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"]
42248       ],
42249       [
42250         "Cape Verde (Kabu Verdi)",
42251         "cv",
42252         "238"
42253       ],
42254       [
42255         "Caribbean Netherlands",
42256         "bq",
42257         "599",
42258         1
42259       ],
42260       [
42261         "Cayman Islands",
42262         "ky",
42263         "1345"
42264       ],
42265       [
42266         "Central African Republic (République centrafricaine)",
42267         "cf",
42268         "236"
42269       ],
42270       [
42271         "Chad (Tchad)",
42272         "td",
42273         "235"
42274       ],
42275       [
42276         "Chile",
42277         "cl",
42278         "56"
42279       ],
42280       [
42281         "China (中国)",
42282         "cn",
42283         "86"
42284       ],
42285       [
42286         "Christmas Island",
42287         "cx",
42288         "61",
42289         2
42290       ],
42291       [
42292         "Cocos (Keeling) Islands",
42293         "cc",
42294         "61",
42295         1
42296       ],
42297       [
42298         "Colombia",
42299         "co",
42300         "57"
42301       ],
42302       [
42303         "Comoros (‫جزر القمر‬‎)",
42304         "km",
42305         "269"
42306       ],
42307       [
42308         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42309         "cd",
42310         "243"
42311       ],
42312       [
42313         "Congo (Republic) (Congo-Brazzaville)",
42314         "cg",
42315         "242"
42316       ],
42317       [
42318         "Cook Islands",
42319         "ck",
42320         "682"
42321       ],
42322       [
42323         "Costa Rica",
42324         "cr",
42325         "506"
42326       ],
42327       [
42328         "Côte d’Ivoire",
42329         "ci",
42330         "225"
42331       ],
42332       [
42333         "Croatia (Hrvatska)",
42334         "hr",
42335         "385"
42336       ],
42337       [
42338         "Cuba",
42339         "cu",
42340         "53"
42341       ],
42342       [
42343         "Curaçao",
42344         "cw",
42345         "599",
42346         0
42347       ],
42348       [
42349         "Cyprus (Κύπρος)",
42350         "cy",
42351         "357"
42352       ],
42353       [
42354         "Czech Republic (Česká republika)",
42355         "cz",
42356         "420"
42357       ],
42358       [
42359         "Denmark (Danmark)",
42360         "dk",
42361         "45"
42362       ],
42363       [
42364         "Djibouti",
42365         "dj",
42366         "253"
42367       ],
42368       [
42369         "Dominica",
42370         "dm",
42371         "1767"
42372       ],
42373       [
42374         "Dominican Republic (República Dominicana)",
42375         "do",
42376         "1",
42377         2,
42378         ["809", "829", "849"]
42379       ],
42380       [
42381         "Ecuador",
42382         "ec",
42383         "593"
42384       ],
42385       [
42386         "Egypt (‫مصر‬‎)",
42387         "eg",
42388         "20"
42389       ],
42390       [
42391         "El Salvador",
42392         "sv",
42393         "503"
42394       ],
42395       [
42396         "Equatorial Guinea (Guinea Ecuatorial)",
42397         "gq",
42398         "240"
42399       ],
42400       [
42401         "Eritrea",
42402         "er",
42403         "291"
42404       ],
42405       [
42406         "Estonia (Eesti)",
42407         "ee",
42408         "372"
42409       ],
42410       [
42411         "Ethiopia",
42412         "et",
42413         "251"
42414       ],
42415       [
42416         "Falkland Islands (Islas Malvinas)",
42417         "fk",
42418         "500"
42419       ],
42420       [
42421         "Faroe Islands (Føroyar)",
42422         "fo",
42423         "298"
42424       ],
42425       [
42426         "Fiji",
42427         "fj",
42428         "679"
42429       ],
42430       [
42431         "Finland (Suomi)",
42432         "fi",
42433         "358",
42434         0
42435       ],
42436       [
42437         "France",
42438         "fr",
42439         "33"
42440       ],
42441       [
42442         "French Guiana (Guyane française)",
42443         "gf",
42444         "594"
42445       ],
42446       [
42447         "French Polynesia (Polynésie française)",
42448         "pf",
42449         "689"
42450       ],
42451       [
42452         "Gabon",
42453         "ga",
42454         "241"
42455       ],
42456       [
42457         "Gambia",
42458         "gm",
42459         "220"
42460       ],
42461       [
42462         "Georgia (საქართველო)",
42463         "ge",
42464         "995"
42465       ],
42466       [
42467         "Germany (Deutschland)",
42468         "de",
42469         "49"
42470       ],
42471       [
42472         "Ghana (Gaana)",
42473         "gh",
42474         "233"
42475       ],
42476       [
42477         "Gibraltar",
42478         "gi",
42479         "350"
42480       ],
42481       [
42482         "Greece (Ελλάδα)",
42483         "gr",
42484         "30"
42485       ],
42486       [
42487         "Greenland (Kalaallit Nunaat)",
42488         "gl",
42489         "299"
42490       ],
42491       [
42492         "Grenada",
42493         "gd",
42494         "1473"
42495       ],
42496       [
42497         "Guadeloupe",
42498         "gp",
42499         "590",
42500         0
42501       ],
42502       [
42503         "Guam",
42504         "gu",
42505         "1671"
42506       ],
42507       [
42508         "Guatemala",
42509         "gt",
42510         "502"
42511       ],
42512       [
42513         "Guernsey",
42514         "gg",
42515         "44",
42516         1
42517       ],
42518       [
42519         "Guinea (Guinée)",
42520         "gn",
42521         "224"
42522       ],
42523       [
42524         "Guinea-Bissau (Guiné Bissau)",
42525         "gw",
42526         "245"
42527       ],
42528       [
42529         "Guyana",
42530         "gy",
42531         "592"
42532       ],
42533       [
42534         "Haiti",
42535         "ht",
42536         "509"
42537       ],
42538       [
42539         "Honduras",
42540         "hn",
42541         "504"
42542       ],
42543       [
42544         "Hong Kong (香港)",
42545         "hk",
42546         "852"
42547       ],
42548       [
42549         "Hungary (Magyarország)",
42550         "hu",
42551         "36"
42552       ],
42553       [
42554         "Iceland (Ísland)",
42555         "is",
42556         "354"
42557       ],
42558       [
42559         "India (भारत)",
42560         "in",
42561         "91"
42562       ],
42563       [
42564         "Indonesia",
42565         "id",
42566         "62"
42567       ],
42568       [
42569         "Iran (‫ایران‬‎)",
42570         "ir",
42571         "98"
42572       ],
42573       [
42574         "Iraq (‫العراق‬‎)",
42575         "iq",
42576         "964"
42577       ],
42578       [
42579         "Ireland",
42580         "ie",
42581         "353"
42582       ],
42583       [
42584         "Isle of Man",
42585         "im",
42586         "44",
42587         2
42588       ],
42589       [
42590         "Israel (‫ישראל‬‎)",
42591         "il",
42592         "972"
42593       ],
42594       [
42595         "Italy (Italia)",
42596         "it",
42597         "39",
42598         0
42599       ],
42600       [
42601         "Jamaica",
42602         "jm",
42603         "1876"
42604       ],
42605       [
42606         "Japan (日本)",
42607         "jp",
42608         "81"
42609       ],
42610       [
42611         "Jersey",
42612         "je",
42613         "44",
42614         3
42615       ],
42616       [
42617         "Jordan (‫الأردن‬‎)",
42618         "jo",
42619         "962"
42620       ],
42621       [
42622         "Kazakhstan (Казахстан)",
42623         "kz",
42624         "7",
42625         1
42626       ],
42627       [
42628         "Kenya",
42629         "ke",
42630         "254"
42631       ],
42632       [
42633         "Kiribati",
42634         "ki",
42635         "686"
42636       ],
42637       [
42638         "Kosovo",
42639         "xk",
42640         "383"
42641       ],
42642       [
42643         "Kuwait (‫الكويت‬‎)",
42644         "kw",
42645         "965"
42646       ],
42647       [
42648         "Kyrgyzstan (Кыргызстан)",
42649         "kg",
42650         "996"
42651       ],
42652       [
42653         "Laos (ລາວ)",
42654         "la",
42655         "856"
42656       ],
42657       [
42658         "Latvia (Latvija)",
42659         "lv",
42660         "371"
42661       ],
42662       [
42663         "Lebanon (‫لبنان‬‎)",
42664         "lb",
42665         "961"
42666       ],
42667       [
42668         "Lesotho",
42669         "ls",
42670         "266"
42671       ],
42672       [
42673         "Liberia",
42674         "lr",
42675         "231"
42676       ],
42677       [
42678         "Libya (‫ليبيا‬‎)",
42679         "ly",
42680         "218"
42681       ],
42682       [
42683         "Liechtenstein",
42684         "li",
42685         "423"
42686       ],
42687       [
42688         "Lithuania (Lietuva)",
42689         "lt",
42690         "370"
42691       ],
42692       [
42693         "Luxembourg",
42694         "lu",
42695         "352"
42696       ],
42697       [
42698         "Macau (澳門)",
42699         "mo",
42700         "853"
42701       ],
42702       [
42703         "Macedonia (FYROM) (Македонија)",
42704         "mk",
42705         "389"
42706       ],
42707       [
42708         "Madagascar (Madagasikara)",
42709         "mg",
42710         "261"
42711       ],
42712       [
42713         "Malawi",
42714         "mw",
42715         "265"
42716       ],
42717       [
42718         "Malaysia",
42719         "my",
42720         "60"
42721       ],
42722       [
42723         "Maldives",
42724         "mv",
42725         "960"
42726       ],
42727       [
42728         "Mali",
42729         "ml",
42730         "223"
42731       ],
42732       [
42733         "Malta",
42734         "mt",
42735         "356"
42736       ],
42737       [
42738         "Marshall Islands",
42739         "mh",
42740         "692"
42741       ],
42742       [
42743         "Martinique",
42744         "mq",
42745         "596"
42746       ],
42747       [
42748         "Mauritania (‫موريتانيا‬‎)",
42749         "mr",
42750         "222"
42751       ],
42752       [
42753         "Mauritius (Moris)",
42754         "mu",
42755         "230"
42756       ],
42757       [
42758         "Mayotte",
42759         "yt",
42760         "262",
42761         1
42762       ],
42763       [
42764         "Mexico (México)",
42765         "mx",
42766         "52"
42767       ],
42768       [
42769         "Micronesia",
42770         "fm",
42771         "691"
42772       ],
42773       [
42774         "Moldova (Republica Moldova)",
42775         "md",
42776         "373"
42777       ],
42778       [
42779         "Monaco",
42780         "mc",
42781         "377"
42782       ],
42783       [
42784         "Mongolia (Монгол)",
42785         "mn",
42786         "976"
42787       ],
42788       [
42789         "Montenegro (Crna Gora)",
42790         "me",
42791         "382"
42792       ],
42793       [
42794         "Montserrat",
42795         "ms",
42796         "1664"
42797       ],
42798       [
42799         "Morocco (‫المغرب‬‎)",
42800         "ma",
42801         "212",
42802         0
42803       ],
42804       [
42805         "Mozambique (Moçambique)",
42806         "mz",
42807         "258"
42808       ],
42809       [
42810         "Myanmar (Burma) (မြန်မာ)",
42811         "mm",
42812         "95"
42813       ],
42814       [
42815         "Namibia (Namibië)",
42816         "na",
42817         "264"
42818       ],
42819       [
42820         "Nauru",
42821         "nr",
42822         "674"
42823       ],
42824       [
42825         "Nepal (नेपाल)",
42826         "np",
42827         "977"
42828       ],
42829       [
42830         "Netherlands (Nederland)",
42831         "nl",
42832         "31"
42833       ],
42834       [
42835         "New Caledonia (Nouvelle-Calédonie)",
42836         "nc",
42837         "687"
42838       ],
42839       [
42840         "New Zealand",
42841         "nz",
42842         "64"
42843       ],
42844       [
42845         "Nicaragua",
42846         "ni",
42847         "505"
42848       ],
42849       [
42850         "Niger (Nijar)",
42851         "ne",
42852         "227"
42853       ],
42854       [
42855         "Nigeria",
42856         "ng",
42857         "234"
42858       ],
42859       [
42860         "Niue",
42861         "nu",
42862         "683"
42863       ],
42864       [
42865         "Norfolk Island",
42866         "nf",
42867         "672"
42868       ],
42869       [
42870         "North Korea (조선 민주주의 인민 공화국)",
42871         "kp",
42872         "850"
42873       ],
42874       [
42875         "Northern Mariana Islands",
42876         "mp",
42877         "1670"
42878       ],
42879       [
42880         "Norway (Norge)",
42881         "no",
42882         "47",
42883         0
42884       ],
42885       [
42886         "Oman (‫عُمان‬‎)",
42887         "om",
42888         "968"
42889       ],
42890       [
42891         "Pakistan (‫پاکستان‬‎)",
42892         "pk",
42893         "92"
42894       ],
42895       [
42896         "Palau",
42897         "pw",
42898         "680"
42899       ],
42900       [
42901         "Palestine (‫فلسطين‬‎)",
42902         "ps",
42903         "970"
42904       ],
42905       [
42906         "Panama (Panamá)",
42907         "pa",
42908         "507"
42909       ],
42910       [
42911         "Papua New Guinea",
42912         "pg",
42913         "675"
42914       ],
42915       [
42916         "Paraguay",
42917         "py",
42918         "595"
42919       ],
42920       [
42921         "Peru (Perú)",
42922         "pe",
42923         "51"
42924       ],
42925       [
42926         "Philippines",
42927         "ph",
42928         "63"
42929       ],
42930       [
42931         "Poland (Polska)",
42932         "pl",
42933         "48"
42934       ],
42935       [
42936         "Portugal",
42937         "pt",
42938         "351"
42939       ],
42940       [
42941         "Puerto Rico",
42942         "pr",
42943         "1",
42944         3,
42945         ["787", "939"]
42946       ],
42947       [
42948         "Qatar (‫قطر‬‎)",
42949         "qa",
42950         "974"
42951       ],
42952       [
42953         "Réunion (La Réunion)",
42954         "re",
42955         "262",
42956         0
42957       ],
42958       [
42959         "Romania (România)",
42960         "ro",
42961         "40"
42962       ],
42963       [
42964         "Russia (Россия)",
42965         "ru",
42966         "7",
42967         0
42968       ],
42969       [
42970         "Rwanda",
42971         "rw",
42972         "250"
42973       ],
42974       [
42975         "Saint Barthélemy",
42976         "bl",
42977         "590",
42978         1
42979       ],
42980       [
42981         "Saint Helena",
42982         "sh",
42983         "290"
42984       ],
42985       [
42986         "Saint Kitts and Nevis",
42987         "kn",
42988         "1869"
42989       ],
42990       [
42991         "Saint Lucia",
42992         "lc",
42993         "1758"
42994       ],
42995       [
42996         "Saint Martin (Saint-Martin (partie française))",
42997         "mf",
42998         "590",
42999         2
43000       ],
43001       [
43002         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43003         "pm",
43004         "508"
43005       ],
43006       [
43007         "Saint Vincent and the Grenadines",
43008         "vc",
43009         "1784"
43010       ],
43011       [
43012         "Samoa",
43013         "ws",
43014         "685"
43015       ],
43016       [
43017         "San Marino",
43018         "sm",
43019         "378"
43020       ],
43021       [
43022         "São Tomé and Príncipe (São Tomé e Príncipe)",
43023         "st",
43024         "239"
43025       ],
43026       [
43027         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43028         "sa",
43029         "966"
43030       ],
43031       [
43032         "Senegal (Sénégal)",
43033         "sn",
43034         "221"
43035       ],
43036       [
43037         "Serbia (Србија)",
43038         "rs",
43039         "381"
43040       ],
43041       [
43042         "Seychelles",
43043         "sc",
43044         "248"
43045       ],
43046       [
43047         "Sierra Leone",
43048         "sl",
43049         "232"
43050       ],
43051       [
43052         "Singapore",
43053         "sg",
43054         "65"
43055       ],
43056       [
43057         "Sint Maarten",
43058         "sx",
43059         "1721"
43060       ],
43061       [
43062         "Slovakia (Slovensko)",
43063         "sk",
43064         "421"
43065       ],
43066       [
43067         "Slovenia (Slovenija)",
43068         "si",
43069         "386"
43070       ],
43071       [
43072         "Solomon Islands",
43073         "sb",
43074         "677"
43075       ],
43076       [
43077         "Somalia (Soomaaliya)",
43078         "so",
43079         "252"
43080       ],
43081       [
43082         "South Africa",
43083         "za",
43084         "27"
43085       ],
43086       [
43087         "South Korea (대한민국)",
43088         "kr",
43089         "82"
43090       ],
43091       [
43092         "South Sudan (‫جنوب السودان‬‎)",
43093         "ss",
43094         "211"
43095       ],
43096       [
43097         "Spain (España)",
43098         "es",
43099         "34"
43100       ],
43101       [
43102         "Sri Lanka (ශ්‍රී ලංකාව)",
43103         "lk",
43104         "94"
43105       ],
43106       [
43107         "Sudan (‫السودان‬‎)",
43108         "sd",
43109         "249"
43110       ],
43111       [
43112         "Suriname",
43113         "sr",
43114         "597"
43115       ],
43116       [
43117         "Svalbard and Jan Mayen",
43118         "sj",
43119         "47",
43120         1
43121       ],
43122       [
43123         "Swaziland",
43124         "sz",
43125         "268"
43126       ],
43127       [
43128         "Sweden (Sverige)",
43129         "se",
43130         "46"
43131       ],
43132       [
43133         "Switzerland (Schweiz)",
43134         "ch",
43135         "41"
43136       ],
43137       [
43138         "Syria (‫سوريا‬‎)",
43139         "sy",
43140         "963"
43141       ],
43142       [
43143         "Taiwan (台灣)",
43144         "tw",
43145         "886"
43146       ],
43147       [
43148         "Tajikistan",
43149         "tj",
43150         "992"
43151       ],
43152       [
43153         "Tanzania",
43154         "tz",
43155         "255"
43156       ],
43157       [
43158         "Thailand (ไทย)",
43159         "th",
43160         "66"
43161       ],
43162       [
43163         "Timor-Leste",
43164         "tl",
43165         "670"
43166       ],
43167       [
43168         "Togo",
43169         "tg",
43170         "228"
43171       ],
43172       [
43173         "Tokelau",
43174         "tk",
43175         "690"
43176       ],
43177       [
43178         "Tonga",
43179         "to",
43180         "676"
43181       ],
43182       [
43183         "Trinidad and Tobago",
43184         "tt",
43185         "1868"
43186       ],
43187       [
43188         "Tunisia (‫تونس‬‎)",
43189         "tn",
43190         "216"
43191       ],
43192       [
43193         "Turkey (Türkiye)",
43194         "tr",
43195         "90"
43196       ],
43197       [
43198         "Turkmenistan",
43199         "tm",
43200         "993"
43201       ],
43202       [
43203         "Turks and Caicos Islands",
43204         "tc",
43205         "1649"
43206       ],
43207       [
43208         "Tuvalu",
43209         "tv",
43210         "688"
43211       ],
43212       [
43213         "U.S. Virgin Islands",
43214         "vi",
43215         "1340"
43216       ],
43217       [
43218         "Uganda",
43219         "ug",
43220         "256"
43221       ],
43222       [
43223         "Ukraine (Україна)",
43224         "ua",
43225         "380"
43226       ],
43227       [
43228         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43229         "ae",
43230         "971"
43231       ],
43232       [
43233         "United Kingdom",
43234         "gb",
43235         "44",
43236         0
43237       ],
43238       [
43239         "United States",
43240         "us",
43241         "1",
43242         0
43243       ],
43244       [
43245         "Uruguay",
43246         "uy",
43247         "598"
43248       ],
43249       [
43250         "Uzbekistan (Oʻzbekiston)",
43251         "uz",
43252         "998"
43253       ],
43254       [
43255         "Vanuatu",
43256         "vu",
43257         "678"
43258       ],
43259       [
43260         "Vatican City (Città del Vaticano)",
43261         "va",
43262         "39",
43263         1
43264       ],
43265       [
43266         "Venezuela",
43267         "ve",
43268         "58"
43269       ],
43270       [
43271         "Vietnam (Việt Nam)",
43272         "vn",
43273         "84"
43274       ],
43275       [
43276         "Wallis and Futuna (Wallis-et-Futuna)",
43277         "wf",
43278         "681"
43279       ],
43280       [
43281         "Western Sahara (‫الصحراء الغربية‬‎)",
43282         "eh",
43283         "212",
43284         1
43285       ],
43286       [
43287         "Yemen (‫اليمن‬‎)",
43288         "ye",
43289         "967"
43290       ],
43291       [
43292         "Zambia",
43293         "zm",
43294         "260"
43295       ],
43296       [
43297         "Zimbabwe",
43298         "zw",
43299         "263"
43300       ],
43301       [
43302         "Åland Islands",
43303         "ax",
43304         "358",
43305         1
43306       ]
43307   ];
43308   
43309   return d;
43310 }/**
43311 *    This script refer to:
43312 *    Title: International Telephone Input
43313 *    Author: Jack O'Connor
43314 *    Code version:  v12.1.12
43315 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43316 **/
43317
43318 /**
43319  * @class Roo.bootstrap.form.PhoneInput
43320  * @extends Roo.bootstrap.form.TriggerField
43321  * An input with International dial-code selection
43322  
43323  * @cfg {String} defaultDialCode default '+852'
43324  * @cfg {Array} preferedCountries default []
43325   
43326  * @constructor
43327  * Create a new PhoneInput.
43328  * @param {Object} config Configuration options
43329  */
43330
43331 Roo.bootstrap.form.PhoneInput = function(config) {
43332     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43333 };
43334
43335 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43336         /**
43337         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43338         */
43339         listWidth: undefined,
43340         
43341         selectedClass: 'active',
43342         
43343         invalidClass : "has-warning",
43344         
43345         validClass: 'has-success',
43346         
43347         allowed: '0123456789',
43348         
43349         max_length: 15,
43350         
43351         /**
43352          * @cfg {String} defaultDialCode The default dial code when initializing the input
43353          */
43354         defaultDialCode: '+852',
43355         
43356         /**
43357          * @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
43358          */
43359         preferedCountries: false,
43360         
43361         getAutoCreate : function()
43362         {
43363             var data = Roo.bootstrap.form.PhoneInputData();
43364             var align = this.labelAlign || this.parentLabelAlign();
43365             var id = Roo.id();
43366             
43367             this.allCountries = [];
43368             this.dialCodeMapping = [];
43369             
43370             for (var i = 0; i < data.length; i++) {
43371               var c = data[i];
43372               this.allCountries[i] = {
43373                 name: c[0],
43374                 iso2: c[1],
43375                 dialCode: c[2],
43376                 priority: c[3] || 0,
43377                 areaCodes: c[4] || null
43378               };
43379               this.dialCodeMapping[c[2]] = {
43380                   name: c[0],
43381                   iso2: c[1],
43382                   priority: c[3] || 0,
43383                   areaCodes: c[4] || null
43384               };
43385             }
43386             
43387             var cfg = {
43388                 cls: 'form-group',
43389                 cn: []
43390             };
43391             
43392             var input =  {
43393                 tag: 'input',
43394                 id : id,
43395                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43396                 maxlength: this.max_length,
43397                 cls : 'form-control tel-input',
43398                 autocomplete: 'new-password'
43399             };
43400             
43401             var hiddenInput = {
43402                 tag: 'input',
43403                 type: 'hidden',
43404                 cls: 'hidden-tel-input'
43405             };
43406             
43407             if (this.name) {
43408                 hiddenInput.name = this.name;
43409             }
43410             
43411             if (this.disabled) {
43412                 input.disabled = true;
43413             }
43414             
43415             var flag_container = {
43416                 tag: 'div',
43417                 cls: 'flag-box',
43418                 cn: [
43419                     {
43420                         tag: 'div',
43421                         cls: 'flag'
43422                     },
43423                     {
43424                         tag: 'div',
43425                         cls: 'caret'
43426                     }
43427                 ]
43428             };
43429             
43430             var box = {
43431                 tag: 'div',
43432                 cls: this.hasFeedback ? 'has-feedback' : '',
43433                 cn: [
43434                     hiddenInput,
43435                     input,
43436                     {
43437                         tag: 'input',
43438                         cls: 'dial-code-holder',
43439                         disabled: true
43440                     }
43441                 ]
43442             };
43443             
43444             var container = {
43445                 cls: 'roo-select2-container input-group',
43446                 cn: [
43447                     flag_container,
43448                     box
43449                 ]
43450             };
43451             
43452             if (this.fieldLabel.length) {
43453                 var indicator = {
43454                     tag: 'i',
43455                     tooltip: 'This field is required'
43456                 };
43457                 
43458                 var label = {
43459                     tag: 'label',
43460                     'for':  id,
43461                     cls: 'control-label',
43462                     cn: []
43463                 };
43464                 
43465                 var label_text = {
43466                     tag: 'span',
43467                     html: this.fieldLabel
43468                 };
43469                 
43470                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43471                 label.cn = [
43472                     indicator,
43473                     label_text
43474                 ];
43475                 
43476                 if(this.indicatorpos == 'right') {
43477                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43478                     label.cn = [
43479                         label_text,
43480                         indicator
43481                     ];
43482                 }
43483                 
43484                 if(align == 'left') {
43485                     container = {
43486                         tag: 'div',
43487                         cn: [
43488                             container
43489                         ]
43490                     };
43491                     
43492                     if(this.labelWidth > 12){
43493                         label.style = "width: " + this.labelWidth + 'px';
43494                     }
43495                     if(this.labelWidth < 13 && this.labelmd == 0){
43496                         this.labelmd = this.labelWidth;
43497                     }
43498                     if(this.labellg > 0){
43499                         label.cls += ' col-lg-' + this.labellg;
43500                         input.cls += ' col-lg-' + (12 - this.labellg);
43501                     }
43502                     if(this.labelmd > 0){
43503                         label.cls += ' col-md-' + this.labelmd;
43504                         container.cls += ' col-md-' + (12 - this.labelmd);
43505                     }
43506                     if(this.labelsm > 0){
43507                         label.cls += ' col-sm-' + this.labelsm;
43508                         container.cls += ' col-sm-' + (12 - this.labelsm);
43509                     }
43510                     if(this.labelxs > 0){
43511                         label.cls += ' col-xs-' + this.labelxs;
43512                         container.cls += ' col-xs-' + (12 - this.labelxs);
43513                     }
43514                 }
43515             }
43516             
43517             cfg.cn = [
43518                 label,
43519                 container
43520             ];
43521             
43522             var settings = this;
43523             
43524             ['xs','sm','md','lg'].map(function(size){
43525                 if (settings[size]) {
43526                     cfg.cls += ' col-' + size + '-' + settings[size];
43527                 }
43528             });
43529             
43530             this.store = new Roo.data.Store({
43531                 proxy : new Roo.data.MemoryProxy({}),
43532                 reader : new Roo.data.JsonReader({
43533                     fields : [
43534                         {
43535                             'name' : 'name',
43536                             'type' : 'string'
43537                         },
43538                         {
43539                             'name' : 'iso2',
43540                             'type' : 'string'
43541                         },
43542                         {
43543                             'name' : 'dialCode',
43544                             'type' : 'string'
43545                         },
43546                         {
43547                             'name' : 'priority',
43548                             'type' : 'string'
43549                         },
43550                         {
43551                             'name' : 'areaCodes',
43552                             'type' : 'string'
43553                         }
43554                     ]
43555                 })
43556             });
43557             
43558             if(!this.preferedCountries) {
43559                 this.preferedCountries = [
43560                     'hk',
43561                     'gb',
43562                     'us'
43563                 ];
43564             }
43565             
43566             var p = this.preferedCountries.reverse();
43567             
43568             if(p) {
43569                 for (var i = 0; i < p.length; i++) {
43570                     for (var j = 0; j < this.allCountries.length; j++) {
43571                         if(this.allCountries[j].iso2 == p[i]) {
43572                             var t = this.allCountries[j];
43573                             this.allCountries.splice(j,1);
43574                             this.allCountries.unshift(t);
43575                         }
43576                     } 
43577                 }
43578             }
43579             
43580             this.store.proxy.data = {
43581                 success: true,
43582                 data: this.allCountries
43583             };
43584             
43585             return cfg;
43586         },
43587         
43588         initEvents : function()
43589         {
43590             this.createList();
43591             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43592             
43593             this.indicator = this.indicatorEl();
43594             this.flag = this.flagEl();
43595             this.dialCodeHolder = this.dialCodeHolderEl();
43596             
43597             this.trigger = this.el.select('div.flag-box',true).first();
43598             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43599             
43600             var _this = this;
43601             
43602             (function(){
43603                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43604                 _this.list.setWidth(lw);
43605             }).defer(100);
43606             
43607             this.list.on('mouseover', this.onViewOver, this);
43608             this.list.on('mousemove', this.onViewMove, this);
43609             this.inputEl().on("keyup", this.onKeyUp, this);
43610             this.inputEl().on("keypress", this.onKeyPress, this);
43611             
43612             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43613
43614             this.view = new Roo.View(this.list, this.tpl, {
43615                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43616             });
43617             
43618             this.view.on('click', this.onViewClick, this);
43619             this.setValue(this.defaultDialCode);
43620         },
43621         
43622         onTriggerClick : function(e)
43623         {
43624             Roo.log('trigger click');
43625             if(this.disabled){
43626                 return;
43627             }
43628             
43629             if(this.isExpanded()){
43630                 this.collapse();
43631                 this.hasFocus = false;
43632             }else {
43633                 this.store.load({});
43634                 this.hasFocus = true;
43635                 this.expand();
43636             }
43637         },
43638         
43639         isExpanded : function()
43640         {
43641             return this.list.isVisible();
43642         },
43643         
43644         collapse : function()
43645         {
43646             if(!this.isExpanded()){
43647                 return;
43648             }
43649             this.list.hide();
43650             Roo.get(document).un('mousedown', this.collapseIf, this);
43651             Roo.get(document).un('mousewheel', this.collapseIf, this);
43652             this.fireEvent('collapse', this);
43653             this.validate();
43654         },
43655         
43656         expand : function()
43657         {
43658             Roo.log('expand');
43659
43660             if(this.isExpanded() || !this.hasFocus){
43661                 return;
43662             }
43663             
43664             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43665             this.list.setWidth(lw);
43666             
43667             this.list.show();
43668             this.restrictHeight();
43669             
43670             Roo.get(document).on('mousedown', this.collapseIf, this);
43671             Roo.get(document).on('mousewheel', this.collapseIf, this);
43672             
43673             this.fireEvent('expand', this);
43674         },
43675         
43676         restrictHeight : function()
43677         {
43678             this.list.alignTo(this.inputEl(), this.listAlign);
43679             this.list.alignTo(this.inputEl(), this.listAlign);
43680         },
43681         
43682         onViewOver : function(e, t)
43683         {
43684             if(this.inKeyMode){
43685                 return;
43686             }
43687             var item = this.view.findItemFromChild(t);
43688             
43689             if(item){
43690                 var index = this.view.indexOf(item);
43691                 this.select(index, false);
43692             }
43693         },
43694
43695         // private
43696         onViewClick : function(view, doFocus, el, e)
43697         {
43698             var index = this.view.getSelectedIndexes()[0];
43699             
43700             var r = this.store.getAt(index);
43701             
43702             if(r){
43703                 this.onSelect(r, index);
43704             }
43705             if(doFocus !== false && !this.blockFocus){
43706                 this.inputEl().focus();
43707             }
43708         },
43709         
43710         onViewMove : function(e, t)
43711         {
43712             this.inKeyMode = false;
43713         },
43714         
43715         select : function(index, scrollIntoView)
43716         {
43717             this.selectedIndex = index;
43718             this.view.select(index);
43719             if(scrollIntoView !== false){
43720                 var el = this.view.getNode(index);
43721                 if(el){
43722                     this.list.scrollChildIntoView(el, false);
43723                 }
43724             }
43725         },
43726         
43727         createList : function()
43728         {
43729             this.list = Roo.get(document.body).createChild({
43730                 tag: 'ul',
43731                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43732                 style: 'display:none'
43733             });
43734             
43735             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43736         },
43737         
43738         collapseIf : function(e)
43739         {
43740             var in_combo  = e.within(this.el);
43741             var in_list =  e.within(this.list);
43742             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43743             
43744             if (in_combo || in_list || is_list) {
43745                 return;
43746             }
43747             this.collapse();
43748         },
43749         
43750         onSelect : function(record, index)
43751         {
43752             if(this.fireEvent('beforeselect', this, record, index) !== false){
43753                 
43754                 this.setFlagClass(record.data.iso2);
43755                 this.setDialCode(record.data.dialCode);
43756                 this.hasFocus = false;
43757                 this.collapse();
43758                 this.fireEvent('select', this, record, index);
43759             }
43760         },
43761         
43762         flagEl : function()
43763         {
43764             var flag = this.el.select('div.flag',true).first();
43765             if(!flag){
43766                 return false;
43767             }
43768             return flag;
43769         },
43770         
43771         dialCodeHolderEl : function()
43772         {
43773             var d = this.el.select('input.dial-code-holder',true).first();
43774             if(!d){
43775                 return false;
43776             }
43777             return d;
43778         },
43779         
43780         setDialCode : function(v)
43781         {
43782             this.dialCodeHolder.dom.value = '+'+v;
43783         },
43784         
43785         setFlagClass : function(n)
43786         {
43787             this.flag.dom.className = 'flag '+n;
43788         },
43789         
43790         getValue : function()
43791         {
43792             var v = this.inputEl().getValue();
43793             if(this.dialCodeHolder) {
43794                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43795             }
43796             return v;
43797         },
43798         
43799         setValue : function(v)
43800         {
43801             var d = this.getDialCode(v);
43802             
43803             //invalid dial code
43804             if(v.length == 0 || !d || d.length == 0) {
43805                 if(this.rendered){
43806                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43807                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43808                 }
43809                 return;
43810             }
43811             
43812             //valid dial code
43813             this.setFlagClass(this.dialCodeMapping[d].iso2);
43814             this.setDialCode(d);
43815             this.inputEl().dom.value = v.replace('+'+d,'');
43816             this.hiddenEl().dom.value = this.getValue();
43817             
43818             this.validate();
43819         },
43820         
43821         getDialCode : function(v)
43822         {
43823             v = v ||  '';
43824             
43825             if (v.length == 0) {
43826                 return this.dialCodeHolder.dom.value;
43827             }
43828             
43829             var dialCode = "";
43830             if (v.charAt(0) != "+") {
43831                 return false;
43832             }
43833             var numericChars = "";
43834             for (var i = 1; i < v.length; i++) {
43835               var c = v.charAt(i);
43836               if (!isNaN(c)) {
43837                 numericChars += c;
43838                 if (this.dialCodeMapping[numericChars]) {
43839                   dialCode = v.substr(1, i);
43840                 }
43841                 if (numericChars.length == 4) {
43842                   break;
43843                 }
43844               }
43845             }
43846             return dialCode;
43847         },
43848         
43849         reset : function()
43850         {
43851             this.setValue(this.defaultDialCode);
43852             this.validate();
43853         },
43854         
43855         hiddenEl : function()
43856         {
43857             return this.el.select('input.hidden-tel-input',true).first();
43858         },
43859         
43860         // after setting val
43861         onKeyUp : function(e){
43862             this.setValue(this.getValue());
43863         },
43864         
43865         onKeyPress : function(e){
43866             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43867                 e.stopEvent();
43868             }
43869         }
43870         
43871 });
43872 /**
43873  * @class Roo.bootstrap.form.MoneyField
43874  * @extends Roo.bootstrap.form.ComboBox
43875  * Bootstrap MoneyField class
43876  * 
43877  * @constructor
43878  * Create a new MoneyField.
43879  * @param {Object} config Configuration options
43880  */
43881
43882 Roo.bootstrap.form.MoneyField = function(config) {
43883     
43884     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43885     
43886 };
43887
43888 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43889     
43890     /**
43891      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43892      */
43893     allowDecimals : true,
43894     /**
43895      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43896      */
43897     decimalSeparator : ".",
43898     /**
43899      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43900      */
43901     decimalPrecision : 0,
43902     /**
43903      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43904      */
43905     allowNegative : true,
43906     /**
43907      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43908      */
43909     allowZero: true,
43910     /**
43911      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43912      */
43913     minValue : Number.NEGATIVE_INFINITY,
43914     /**
43915      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43916      */
43917     maxValue : Number.MAX_VALUE,
43918     /**
43919      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43920      */
43921     minText : "The minimum value for this field is {0}",
43922     /**
43923      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43924      */
43925     maxText : "The maximum value for this field is {0}",
43926     /**
43927      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43928      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43929      */
43930     nanText : "{0} is not a valid number",
43931     /**
43932      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43933      */
43934     castInt : true,
43935     /**
43936      * @cfg {String} defaults currency of the MoneyField
43937      * value should be in lkey
43938      */
43939     defaultCurrency : false,
43940     /**
43941      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43942      */
43943     thousandsDelimiter : false,
43944     /**
43945      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43946      */
43947     max_length: false,
43948     
43949     inputlg : 9,
43950     inputmd : 9,
43951     inputsm : 9,
43952     inputxs : 6,
43953      /**
43954      * @cfg {Roo.data.Store} store  Store to lookup currency??
43955      */
43956     store : false,
43957     
43958     getAutoCreate : function()
43959     {
43960         var align = this.labelAlign || this.parentLabelAlign();
43961         
43962         var id = Roo.id();
43963
43964         var cfg = {
43965             cls: 'form-group',
43966             cn: []
43967         };
43968
43969         var input =  {
43970             tag: 'input',
43971             id : id,
43972             cls : 'form-control roo-money-amount-input',
43973             autocomplete: 'new-password'
43974         };
43975         
43976         var hiddenInput = {
43977             tag: 'input',
43978             type: 'hidden',
43979             id: Roo.id(),
43980             cls: 'hidden-number-input'
43981         };
43982         
43983         if(this.max_length) {
43984             input.maxlength = this.max_length; 
43985         }
43986         
43987         if (this.name) {
43988             hiddenInput.name = this.name;
43989         }
43990
43991         if (this.disabled) {
43992             input.disabled = true;
43993         }
43994
43995         var clg = 12 - this.inputlg;
43996         var cmd = 12 - this.inputmd;
43997         var csm = 12 - this.inputsm;
43998         var cxs = 12 - this.inputxs;
43999         
44000         var container = {
44001             tag : 'div',
44002             cls : 'row roo-money-field',
44003             cn : [
44004                 {
44005                     tag : 'div',
44006                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44007                     cn : [
44008                         {
44009                             tag : 'div',
44010                             cls: 'roo-select2-container input-group',
44011                             cn: [
44012                                 {
44013                                     tag : 'input',
44014                                     cls : 'form-control roo-money-currency-input',
44015                                     autocomplete: 'new-password',
44016                                     readOnly : 1,
44017                                     name : this.currencyName
44018                                 },
44019                                 {
44020                                     tag :'span',
44021                                     cls : 'input-group-addon',
44022                                     cn : [
44023                                         {
44024                                             tag: 'span',
44025                                             cls: 'caret'
44026                                         }
44027                                     ]
44028                                 }
44029                             ]
44030                         }
44031                     ]
44032                 },
44033                 {
44034                     tag : 'div',
44035                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44036                     cn : [
44037                         {
44038                             tag: 'div',
44039                             cls: this.hasFeedback ? 'has-feedback' : '',
44040                             cn: [
44041                                 input
44042                             ]
44043                         }
44044                     ]
44045                 }
44046             ]
44047             
44048         };
44049         
44050         if (this.fieldLabel.length) {
44051             var indicator = {
44052                 tag: 'i',
44053                 tooltip: 'This field is required'
44054             };
44055
44056             var label = {
44057                 tag: 'label',
44058                 'for':  id,
44059                 cls: 'control-label',
44060                 cn: []
44061             };
44062
44063             var label_text = {
44064                 tag: 'span',
44065                 html: this.fieldLabel
44066             };
44067
44068             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44069             label.cn = [
44070                 indicator,
44071                 label_text
44072             ];
44073
44074             if(this.indicatorpos == 'right') {
44075                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44076                 label.cn = [
44077                     label_text,
44078                     indicator
44079                 ];
44080             }
44081
44082             if(align == 'left') {
44083                 container = {
44084                     tag: 'div',
44085                     cn: [
44086                         container
44087                     ]
44088                 };
44089
44090                 if(this.labelWidth > 12){
44091                     label.style = "width: " + this.labelWidth + 'px';
44092                 }
44093                 if(this.labelWidth < 13 && this.labelmd == 0){
44094                     this.labelmd = this.labelWidth;
44095                 }
44096                 if(this.labellg > 0){
44097                     label.cls += ' col-lg-' + this.labellg;
44098                     input.cls += ' col-lg-' + (12 - this.labellg);
44099                 }
44100                 if(this.labelmd > 0){
44101                     label.cls += ' col-md-' + this.labelmd;
44102                     container.cls += ' col-md-' + (12 - this.labelmd);
44103                 }
44104                 if(this.labelsm > 0){
44105                     label.cls += ' col-sm-' + this.labelsm;
44106                     container.cls += ' col-sm-' + (12 - this.labelsm);
44107                 }
44108                 if(this.labelxs > 0){
44109                     label.cls += ' col-xs-' + this.labelxs;
44110                     container.cls += ' col-xs-' + (12 - this.labelxs);
44111                 }
44112             }
44113         }
44114
44115         cfg.cn = [
44116             label,
44117             container,
44118             hiddenInput
44119         ];
44120         
44121         var settings = this;
44122
44123         ['xs','sm','md','lg'].map(function(size){
44124             if (settings[size]) {
44125                 cfg.cls += ' col-' + size + '-' + settings[size];
44126             }
44127         });
44128         
44129         return cfg;
44130     },
44131     
44132     initEvents : function()
44133     {
44134         this.indicator = this.indicatorEl();
44135         
44136         this.initCurrencyEvent();
44137         
44138         this.initNumberEvent();
44139     },
44140     
44141     initCurrencyEvent : function()
44142     {
44143         if (!this.store) {
44144             throw "can not find store for combo";
44145         }
44146         
44147         this.store = Roo.factory(this.store, Roo.data);
44148         this.store.parent = this;
44149         
44150         this.createList();
44151         
44152         this.triggerEl = this.el.select('.input-group-addon', true).first();
44153         
44154         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44155         
44156         var _this = this;
44157         
44158         (function(){
44159             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44160             _this.list.setWidth(lw);
44161         }).defer(100);
44162         
44163         this.list.on('mouseover', this.onViewOver, this);
44164         this.list.on('mousemove', this.onViewMove, this);
44165         this.list.on('scroll', this.onViewScroll, this);
44166         
44167         if(!this.tpl){
44168             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44169         }
44170         
44171         this.view = new Roo.View(this.list, this.tpl, {
44172             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44173         });
44174         
44175         this.view.on('click', this.onViewClick, this);
44176         
44177         this.store.on('beforeload', this.onBeforeLoad, this);
44178         this.store.on('load', this.onLoad, this);
44179         this.store.on('loadexception', this.onLoadException, this);
44180         
44181         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44182             "up" : function(e){
44183                 this.inKeyMode = true;
44184                 this.selectPrev();
44185             },
44186
44187             "down" : function(e){
44188                 if(!this.isExpanded()){
44189                     this.onTriggerClick();
44190                 }else{
44191                     this.inKeyMode = true;
44192                     this.selectNext();
44193                 }
44194             },
44195
44196             "enter" : function(e){
44197                 this.collapse();
44198                 
44199                 if(this.fireEvent("specialkey", this, e)){
44200                     this.onViewClick(false);
44201                 }
44202                 
44203                 return true;
44204             },
44205
44206             "esc" : function(e){
44207                 this.collapse();
44208             },
44209
44210             "tab" : function(e){
44211                 this.collapse();
44212                 
44213                 if(this.fireEvent("specialkey", this, e)){
44214                     this.onViewClick(false);
44215                 }
44216                 
44217                 return true;
44218             },
44219
44220             scope : this,
44221
44222             doRelay : function(foo, bar, hname){
44223                 if(hname == 'down' || this.scope.isExpanded()){
44224                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44225                 }
44226                 return true;
44227             },
44228
44229             forceKeyDown: true
44230         });
44231         
44232         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44233         
44234     },
44235     
44236     initNumberEvent : function(e)
44237     {
44238         this.inputEl().on("keydown" , this.fireKey,  this);
44239         this.inputEl().on("focus", this.onFocus,  this);
44240         this.inputEl().on("blur", this.onBlur,  this);
44241         
44242         this.inputEl().relayEvent('keyup', this);
44243         
44244         if(this.indicator){
44245             this.indicator.addClass('invisible');
44246         }
44247  
44248         this.originalValue = this.getValue();
44249         
44250         if(this.validationEvent == 'keyup'){
44251             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44252             this.inputEl().on('keyup', this.filterValidation, this);
44253         }
44254         else if(this.validationEvent !== false){
44255             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44256         }
44257         
44258         if(this.selectOnFocus){
44259             this.on("focus", this.preFocus, this);
44260             
44261         }
44262         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44263             this.inputEl().on("keypress", this.filterKeys, this);
44264         } else {
44265             this.inputEl().relayEvent('keypress', this);
44266         }
44267         
44268         var allowed = "0123456789";
44269         
44270         if(this.allowDecimals){
44271             allowed += this.decimalSeparator;
44272         }
44273         
44274         if(this.allowNegative){
44275             allowed += "-";
44276         }
44277         
44278         if(this.thousandsDelimiter) {
44279             allowed += ",";
44280         }
44281         
44282         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44283         
44284         var keyPress = function(e){
44285             
44286             var k = e.getKey();
44287             
44288             var c = e.getCharCode();
44289             
44290             if(
44291                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44292                     allowed.indexOf(String.fromCharCode(c)) === -1
44293             ){
44294                 e.stopEvent();
44295                 return;
44296             }
44297             
44298             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44299                 return;
44300             }
44301             
44302             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44303                 e.stopEvent();
44304             }
44305         };
44306         
44307         this.inputEl().on("keypress", keyPress, this);
44308         
44309     },
44310     
44311     onTriggerClick : function(e)
44312     {   
44313         if(this.disabled){
44314             return;
44315         }
44316         
44317         this.page = 0;
44318         this.loadNext = false;
44319         
44320         if(this.isExpanded()){
44321             this.collapse();
44322             return;
44323         }
44324         
44325         this.hasFocus = true;
44326         
44327         if(this.triggerAction == 'all') {
44328             this.doQuery(this.allQuery, true);
44329             return;
44330         }
44331         
44332         this.doQuery(this.getRawValue());
44333     },
44334     
44335     getCurrency : function()
44336     {   
44337         var v = this.currencyEl().getValue();
44338         
44339         return v;
44340     },
44341     
44342     restrictHeight : function()
44343     {
44344         this.list.alignTo(this.currencyEl(), this.listAlign);
44345         this.list.alignTo(this.currencyEl(), this.listAlign);
44346     },
44347     
44348     onViewClick : function(view, doFocus, el, e)
44349     {
44350         var index = this.view.getSelectedIndexes()[0];
44351         
44352         var r = this.store.getAt(index);
44353         
44354         if(r){
44355             this.onSelect(r, index);
44356         }
44357     },
44358     
44359     onSelect : function(record, index){
44360         
44361         if(this.fireEvent('beforeselect', this, record, index) !== false){
44362         
44363             this.setFromCurrencyData(index > -1 ? record.data : false);
44364             
44365             this.collapse();
44366             
44367             this.fireEvent('select', this, record, index);
44368         }
44369     },
44370     
44371     setFromCurrencyData : function(o)
44372     {
44373         var currency = '';
44374         
44375         this.lastCurrency = o;
44376         
44377         if (this.currencyField) {
44378             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44379         } else {
44380             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44381         }
44382         
44383         this.lastSelectionText = currency;
44384         
44385         //setting default currency
44386         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44387             this.setCurrency(this.defaultCurrency);
44388             return;
44389         }
44390         
44391         this.setCurrency(currency);
44392     },
44393     
44394     setFromData : function(o)
44395     {
44396         var c = {};
44397         
44398         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44399         
44400         this.setFromCurrencyData(c);
44401         
44402         var value = '';
44403         
44404         if (this.name) {
44405             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44406         } else {
44407             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44408         }
44409         
44410         this.setValue(value);
44411         
44412     },
44413     
44414     setCurrency : function(v)
44415     {   
44416         this.currencyValue = v;
44417         
44418         if(this.rendered){
44419             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44420             this.validate();
44421         }
44422     },
44423     
44424     setValue : function(v)
44425     {
44426         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44427         
44428         this.value = v;
44429         
44430         if(this.rendered){
44431             
44432             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44433             
44434             this.inputEl().dom.value = (v == '') ? '' :
44435                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44436             
44437             if(!this.allowZero && v === '0') {
44438                 this.hiddenEl().dom.value = '';
44439                 this.inputEl().dom.value = '';
44440             }
44441             
44442             this.validate();
44443         }
44444     },
44445     
44446     getRawValue : function()
44447     {
44448         var v = this.inputEl().getValue();
44449         
44450         return v;
44451     },
44452     
44453     getValue : function()
44454     {
44455         return this.fixPrecision(this.parseValue(this.getRawValue()));
44456     },
44457     
44458     parseValue : function(value)
44459     {
44460         if(this.thousandsDelimiter) {
44461             value += "";
44462             r = new RegExp(",", "g");
44463             value = value.replace(r, "");
44464         }
44465         
44466         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44467         return isNaN(value) ? '' : value;
44468         
44469     },
44470     
44471     fixPrecision : function(value)
44472     {
44473         if(this.thousandsDelimiter) {
44474             value += "";
44475             r = new RegExp(",", "g");
44476             value = value.replace(r, "");
44477         }
44478         
44479         var nan = isNaN(value);
44480         
44481         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44482             return nan ? '' : value;
44483         }
44484         return parseFloat(value).toFixed(this.decimalPrecision);
44485     },
44486     
44487     decimalPrecisionFcn : function(v)
44488     {
44489         return Math.floor(v);
44490     },
44491     
44492     validateValue : function(value)
44493     {
44494         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44495             return false;
44496         }
44497         
44498         var num = this.parseValue(value);
44499         
44500         if(isNaN(num)){
44501             this.markInvalid(String.format(this.nanText, value));
44502             return false;
44503         }
44504         
44505         if(num < this.minValue){
44506             this.markInvalid(String.format(this.minText, this.minValue));
44507             return false;
44508         }
44509         
44510         if(num > this.maxValue){
44511             this.markInvalid(String.format(this.maxText, this.maxValue));
44512             return false;
44513         }
44514         
44515         return true;
44516     },
44517     
44518     validate : function()
44519     {
44520         if(this.disabled || this.allowBlank){
44521             this.markValid();
44522             return true;
44523         }
44524         
44525         var currency = this.getCurrency();
44526         
44527         if(this.validateValue(this.getRawValue()) && currency.length){
44528             this.markValid();
44529             return true;
44530         }
44531         
44532         this.markInvalid();
44533         return false;
44534     },
44535     
44536     getName: function()
44537     {
44538         return this.name;
44539     },
44540     
44541     beforeBlur : function()
44542     {
44543         if(!this.castInt){
44544             return;
44545         }
44546         
44547         var v = this.parseValue(this.getRawValue());
44548         
44549         if(v || v == 0){
44550             this.setValue(v);
44551         }
44552     },
44553     
44554     onBlur : function()
44555     {
44556         this.beforeBlur();
44557         
44558         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44559             //this.el.removeClass(this.focusClass);
44560         }
44561         
44562         this.hasFocus = false;
44563         
44564         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44565             this.validate();
44566         }
44567         
44568         var v = this.getValue();
44569         
44570         if(String(v) !== String(this.startValue)){
44571             this.fireEvent('change', this, v, this.startValue);
44572         }
44573         
44574         this.fireEvent("blur", this);
44575     },
44576     
44577     inputEl : function()
44578     {
44579         return this.el.select('.roo-money-amount-input', true).first();
44580     },
44581     
44582     currencyEl : function()
44583     {
44584         return this.el.select('.roo-money-currency-input', true).first();
44585     },
44586     
44587     hiddenEl : function()
44588     {
44589         return this.el.select('input.hidden-number-input',true).first();
44590     }
44591     
44592 });/**
44593  * @class Roo.bootstrap.BezierSignature
44594  * @extends Roo.bootstrap.Component
44595  * Bootstrap BezierSignature class
44596  * This script refer to:
44597  *    Title: Signature Pad
44598  *    Author: szimek
44599  *    Availability: https://github.com/szimek/signature_pad
44600  *
44601  * @constructor
44602  * Create a new BezierSignature
44603  * @param {Object} config The config object
44604  */
44605
44606 Roo.bootstrap.BezierSignature = function(config){
44607     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44608     this.addEvents({
44609         "resize" : true
44610     });
44611 };
44612
44613 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44614 {
44615      
44616     curve_data: [],
44617     
44618     is_empty: true,
44619     
44620     mouse_btn_down: true,
44621     
44622     /**
44623      * @cfg {int} canvas height
44624      */
44625     canvas_height: '200px',
44626     
44627     /**
44628      * @cfg {float|function} Radius of a single dot.
44629      */ 
44630     dot_size: false,
44631     
44632     /**
44633      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44634      */
44635     min_width: 0.5,
44636     
44637     /**
44638      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44639      */
44640     max_width: 2.5,
44641     
44642     /**
44643      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44644      */
44645     throttle: 16,
44646     
44647     /**
44648      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44649      */
44650     min_distance: 5,
44651     
44652     /**
44653      * @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.
44654      */
44655     bg_color: 'rgba(0, 0, 0, 0)',
44656     
44657     /**
44658      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44659      */
44660     dot_color: 'black',
44661     
44662     /**
44663      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44664      */ 
44665     velocity_filter_weight: 0.7,
44666     
44667     /**
44668      * @cfg {function} Callback when stroke begin. 
44669      */
44670     onBegin: false,
44671     
44672     /**
44673      * @cfg {function} Callback when stroke end.
44674      */
44675     onEnd: false,
44676     
44677     getAutoCreate : function()
44678     {
44679         var cls = 'roo-signature column';
44680         
44681         if(this.cls){
44682             cls += ' ' + this.cls;
44683         }
44684         
44685         var col_sizes = [
44686             'lg',
44687             'md',
44688             'sm',
44689             'xs'
44690         ];
44691         
44692         for(var i = 0; i < col_sizes.length; i++) {
44693             if(this[col_sizes[i]]) {
44694                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44695             }
44696         }
44697         
44698         var cfg = {
44699             tag: 'div',
44700             cls: cls,
44701             cn: [
44702                 {
44703                     tag: 'div',
44704                     cls: 'roo-signature-body',
44705                     cn: [
44706                         {
44707                             tag: 'canvas',
44708                             cls: 'roo-signature-body-canvas',
44709                             height: this.canvas_height,
44710                             width: this.canvas_width
44711                         }
44712                     ]
44713                 },
44714                 {
44715                     tag: 'input',
44716                     type: 'file',
44717                     style: 'display: none'
44718                 }
44719             ]
44720         };
44721         
44722         return cfg;
44723     },
44724     
44725     initEvents: function() 
44726     {
44727         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44728         
44729         var canvas = this.canvasEl();
44730         
44731         // mouse && touch event swapping...
44732         canvas.dom.style.touchAction = 'none';
44733         canvas.dom.style.msTouchAction = 'none';
44734         
44735         this.mouse_btn_down = false;
44736         canvas.on('mousedown', this._handleMouseDown, this);
44737         canvas.on('mousemove', this._handleMouseMove, this);
44738         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44739         
44740         if (window.PointerEvent) {
44741             canvas.on('pointerdown', this._handleMouseDown, this);
44742             canvas.on('pointermove', this._handleMouseMove, this);
44743             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44744         }
44745         
44746         if ('ontouchstart' in window) {
44747             canvas.on('touchstart', this._handleTouchStart, this);
44748             canvas.on('touchmove', this._handleTouchMove, this);
44749             canvas.on('touchend', this._handleTouchEnd, this);
44750         }
44751         
44752         Roo.EventManager.onWindowResize(this.resize, this, true);
44753         
44754         // file input event
44755         this.fileEl().on('change', this.uploadImage, this);
44756         
44757         this.clear();
44758         
44759         this.resize();
44760     },
44761     
44762     resize: function(){
44763         
44764         var canvas = this.canvasEl().dom;
44765         var ctx = this.canvasElCtx();
44766         var img_data = false;
44767         
44768         if(canvas.width > 0) {
44769             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44770         }
44771         // setting canvas width will clean img data
44772         canvas.width = 0;
44773         
44774         var style = window.getComputedStyle ? 
44775             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44776             
44777         var padding_left = parseInt(style.paddingLeft) || 0;
44778         var padding_right = parseInt(style.paddingRight) || 0;
44779         
44780         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44781         
44782         if(img_data) {
44783             ctx.putImageData(img_data, 0, 0);
44784         }
44785     },
44786     
44787     _handleMouseDown: function(e)
44788     {
44789         if (e.browserEvent.which === 1) {
44790             this.mouse_btn_down = true;
44791             this.strokeBegin(e);
44792         }
44793     },
44794     
44795     _handleMouseMove: function (e)
44796     {
44797         if (this.mouse_btn_down) {
44798             this.strokeMoveUpdate(e);
44799         }
44800     },
44801     
44802     _handleMouseUp: function (e)
44803     {
44804         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44805             this.mouse_btn_down = false;
44806             this.strokeEnd(e);
44807         }
44808     },
44809     
44810     _handleTouchStart: function (e) {
44811         
44812         e.preventDefault();
44813         if (e.browserEvent.targetTouches.length === 1) {
44814             // var touch = e.browserEvent.changedTouches[0];
44815             // this.strokeBegin(touch);
44816             
44817              this.strokeBegin(e); // assume e catching the correct xy...
44818         }
44819     },
44820     
44821     _handleTouchMove: function (e) {
44822         e.preventDefault();
44823         // var touch = event.targetTouches[0];
44824         // _this._strokeMoveUpdate(touch);
44825         this.strokeMoveUpdate(e);
44826     },
44827     
44828     _handleTouchEnd: function (e) {
44829         var wasCanvasTouched = e.target === this.canvasEl().dom;
44830         if (wasCanvasTouched) {
44831             e.preventDefault();
44832             // var touch = event.changedTouches[0];
44833             // _this._strokeEnd(touch);
44834             this.strokeEnd(e);
44835         }
44836     },
44837     
44838     reset: function () {
44839         this._lastPoints = [];
44840         this._lastVelocity = 0;
44841         this._lastWidth = (this.min_width + this.max_width) / 2;
44842         this.canvasElCtx().fillStyle = this.dot_color;
44843     },
44844     
44845     strokeMoveUpdate: function(e)
44846     {
44847         this.strokeUpdate(e);
44848         
44849         if (this.throttle) {
44850             this.throttleStroke(this.strokeUpdate, this.throttle);
44851         }
44852         else {
44853             this.strokeUpdate(e);
44854         }
44855     },
44856     
44857     strokeBegin: function(e)
44858     {
44859         var newPointGroup = {
44860             color: this.dot_color,
44861             points: []
44862         };
44863         
44864         if (typeof this.onBegin === 'function') {
44865             this.onBegin(e);
44866         }
44867         
44868         this.curve_data.push(newPointGroup);
44869         this.reset();
44870         this.strokeUpdate(e);
44871     },
44872     
44873     strokeUpdate: function(e)
44874     {
44875         var rect = this.canvasEl().dom.getBoundingClientRect();
44876         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44877         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44878         var lastPoints = lastPointGroup.points;
44879         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44880         var isLastPointTooClose = lastPoint
44881             ? point.distanceTo(lastPoint) <= this.min_distance
44882             : false;
44883         var color = lastPointGroup.color;
44884         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44885             var curve = this.addPoint(point);
44886             if (!lastPoint) {
44887                 this.drawDot({color: color, point: point});
44888             }
44889             else if (curve) {
44890                 this.drawCurve({color: color, curve: curve});
44891             }
44892             lastPoints.push({
44893                 time: point.time,
44894                 x: point.x,
44895                 y: point.y
44896             });
44897         }
44898     },
44899     
44900     strokeEnd: function(e)
44901     {
44902         this.strokeUpdate(e);
44903         if (typeof this.onEnd === 'function') {
44904             this.onEnd(e);
44905         }
44906     },
44907     
44908     addPoint:  function (point) {
44909         var _lastPoints = this._lastPoints;
44910         _lastPoints.push(point);
44911         if (_lastPoints.length > 2) {
44912             if (_lastPoints.length === 3) {
44913                 _lastPoints.unshift(_lastPoints[0]);
44914             }
44915             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44916             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44917             _lastPoints.shift();
44918             return curve;
44919         }
44920         return null;
44921     },
44922     
44923     calculateCurveWidths: function (startPoint, endPoint) {
44924         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44925             (1 - this.velocity_filter_weight) * this._lastVelocity;
44926
44927         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44928         var widths = {
44929             end: newWidth,
44930             start: this._lastWidth
44931         };
44932         
44933         this._lastVelocity = velocity;
44934         this._lastWidth = newWidth;
44935         return widths;
44936     },
44937     
44938     drawDot: function (_a) {
44939         var color = _a.color, point = _a.point;
44940         var ctx = this.canvasElCtx();
44941         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44942         ctx.beginPath();
44943         this.drawCurveSegment(point.x, point.y, width);
44944         ctx.closePath();
44945         ctx.fillStyle = color;
44946         ctx.fill();
44947     },
44948     
44949     drawCurve: function (_a) {
44950         var color = _a.color, curve = _a.curve;
44951         var ctx = this.canvasElCtx();
44952         var widthDelta = curve.endWidth - curve.startWidth;
44953         var drawSteps = Math.floor(curve.length()) * 2;
44954         ctx.beginPath();
44955         ctx.fillStyle = color;
44956         for (var i = 0; i < drawSteps; i += 1) {
44957         var t = i / drawSteps;
44958         var tt = t * t;
44959         var ttt = tt * t;
44960         var u = 1 - t;
44961         var uu = u * u;
44962         var uuu = uu * u;
44963         var x = uuu * curve.startPoint.x;
44964         x += 3 * uu * t * curve.control1.x;
44965         x += 3 * u * tt * curve.control2.x;
44966         x += ttt * curve.endPoint.x;
44967         var y = uuu * curve.startPoint.y;
44968         y += 3 * uu * t * curve.control1.y;
44969         y += 3 * u * tt * curve.control2.y;
44970         y += ttt * curve.endPoint.y;
44971         var width = curve.startWidth + ttt * widthDelta;
44972         this.drawCurveSegment(x, y, width);
44973         }
44974         ctx.closePath();
44975         ctx.fill();
44976     },
44977     
44978     drawCurveSegment: function (x, y, width) {
44979         var ctx = this.canvasElCtx();
44980         ctx.moveTo(x, y);
44981         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44982         this.is_empty = false;
44983     },
44984     
44985     clear: function()
44986     {
44987         var ctx = this.canvasElCtx();
44988         var canvas = this.canvasEl().dom;
44989         ctx.fillStyle = this.bg_color;
44990         ctx.clearRect(0, 0, canvas.width, canvas.height);
44991         ctx.fillRect(0, 0, canvas.width, canvas.height);
44992         this.curve_data = [];
44993         this.reset();
44994         this.is_empty = true;
44995     },
44996     
44997     fileEl: function()
44998     {
44999         return  this.el.select('input',true).first();
45000     },
45001     
45002     canvasEl: function()
45003     {
45004         return this.el.select('canvas',true).first();
45005     },
45006     
45007     canvasElCtx: function()
45008     {
45009         return this.el.select('canvas',true).first().dom.getContext('2d');
45010     },
45011     
45012     getImage: function(type)
45013     {
45014         if(this.is_empty) {
45015             return false;
45016         }
45017         
45018         // encryption ?
45019         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45020     },
45021     
45022     drawFromImage: function(img_src)
45023     {
45024         var img = new Image();
45025         
45026         img.onload = function(){
45027             this.canvasElCtx().drawImage(img, 0, 0);
45028         }.bind(this);
45029         
45030         img.src = img_src;
45031         
45032         this.is_empty = false;
45033     },
45034     
45035     selectImage: function()
45036     {
45037         this.fileEl().dom.click();
45038     },
45039     
45040     uploadImage: function(e)
45041     {
45042         var reader = new FileReader();
45043         
45044         reader.onload = function(e){
45045             var img = new Image();
45046             img.onload = function(){
45047                 this.reset();
45048                 this.canvasElCtx().drawImage(img, 0, 0);
45049             }.bind(this);
45050             img.src = e.target.result;
45051         }.bind(this);
45052         
45053         reader.readAsDataURL(e.target.files[0]);
45054     },
45055     
45056     // Bezier Point Constructor
45057     Point: (function () {
45058         function Point(x, y, time) {
45059             this.x = x;
45060             this.y = y;
45061             this.time = time || Date.now();
45062         }
45063         Point.prototype.distanceTo = function (start) {
45064             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45065         };
45066         Point.prototype.equals = function (other) {
45067             return this.x === other.x && this.y === other.y && this.time === other.time;
45068         };
45069         Point.prototype.velocityFrom = function (start) {
45070             return this.time !== start.time
45071             ? this.distanceTo(start) / (this.time - start.time)
45072             : 0;
45073         };
45074         return Point;
45075     }()),
45076     
45077     
45078     // Bezier Constructor
45079     Bezier: (function () {
45080         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45081             this.startPoint = startPoint;
45082             this.control2 = control2;
45083             this.control1 = control1;
45084             this.endPoint = endPoint;
45085             this.startWidth = startWidth;
45086             this.endWidth = endWidth;
45087         }
45088         Bezier.fromPoints = function (points, widths, scope) {
45089             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45090             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45091             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45092         };
45093         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45094             var dx1 = s1.x - s2.x;
45095             var dy1 = s1.y - s2.y;
45096             var dx2 = s2.x - s3.x;
45097             var dy2 = s2.y - s3.y;
45098             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45099             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45100             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45101             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45102             var dxm = m1.x - m2.x;
45103             var dym = m1.y - m2.y;
45104             var k = l2 / (l1 + l2);
45105             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45106             var tx = s2.x - cm.x;
45107             var ty = s2.y - cm.y;
45108             return {
45109                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45110                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45111             };
45112         };
45113         Bezier.prototype.length = function () {
45114             var steps = 10;
45115             var length = 0;
45116             var px;
45117             var py;
45118             for (var i = 0; i <= steps; i += 1) {
45119                 var t = i / steps;
45120                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45121                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45122                 if (i > 0) {
45123                     var xdiff = cx - px;
45124                     var ydiff = cy - py;
45125                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45126                 }
45127                 px = cx;
45128                 py = cy;
45129             }
45130             return length;
45131         };
45132         Bezier.prototype.point = function (t, start, c1, c2, end) {
45133             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45134             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45135             + (3.0 * c2 * (1.0 - t) * t * t)
45136             + (end * t * t * t);
45137         };
45138         return Bezier;
45139     }()),
45140     
45141     throttleStroke: function(fn, wait) {
45142       if (wait === void 0) { wait = 250; }
45143       var previous = 0;
45144       var timeout = null;
45145       var result;
45146       var storedContext;
45147       var storedArgs;
45148       var later = function () {
45149           previous = Date.now();
45150           timeout = null;
45151           result = fn.apply(storedContext, storedArgs);
45152           if (!timeout) {
45153               storedContext = null;
45154               storedArgs = [];
45155           }
45156       };
45157       return function wrapper() {
45158           var args = [];
45159           for (var _i = 0; _i < arguments.length; _i++) {
45160               args[_i] = arguments[_i];
45161           }
45162           var now = Date.now();
45163           var remaining = wait - (now - previous);
45164           storedContext = this;
45165           storedArgs = args;
45166           if (remaining <= 0 || remaining > wait) {
45167               if (timeout) {
45168                   clearTimeout(timeout);
45169                   timeout = null;
45170               }
45171               previous = now;
45172               result = fn.apply(storedContext, storedArgs);
45173               if (!timeout) {
45174                   storedContext = null;
45175                   storedArgs = [];
45176               }
45177           }
45178           else if (!timeout) {
45179               timeout = window.setTimeout(later, remaining);
45180           }
45181           return result;
45182       };
45183   }
45184   
45185 });
45186
45187  
45188
45189  // old names for form elements
45190 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45191 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45192 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45193 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45194 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45195 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45196 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45197 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45198 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45199 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45200 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45201 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45202 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45203 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45204 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45205 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45206 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45207 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45208 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45209 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45210 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45211 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45212 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45213 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45214 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45215 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45216
45217 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45218 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45219
45220 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45221 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45222
45223 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45224 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45225 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45226 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45227