sync
[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.Form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @builder-top
859  * @children Roo.bootstrap.Component
860  * @parent none
861  * Bootstrap Body class
862  *
863  * @constructor
864  * Create a new body
865  * @param {Object} config The config object
866  */
867
868 Roo.bootstrap.Body = function(config){
869
870     config = config || {};
871
872     Roo.bootstrap.Body.superclass.constructor.call(this, config);
873     this.el = Roo.get(config.el ? config.el : document.body );
874     if (this.cls && this.cls.length) {
875         Roo.get(document.body).addClass(this.cls);
876     }
877 };
878
879 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
880
881     is_body : true,// just to make sure it's constructed?
882
883         autoCreate : {
884         cls: 'container'
885     },
886     onRender : function(ct, position)
887     {
888        /* Roo.log("Roo.bootstrap.Body - onRender");
889         if (this.cls && this.cls.length) {
890             Roo.get(document.body).addClass(this.cls);
891         }
892         // style??? xttr???
893         */
894     }
895
896
897
898
899 });
900 /*
901  * - LGPL
902  *
903  * button group
904  * 
905  */
906
907
908 /**
909  * @class Roo.bootstrap.ButtonGroup
910  * @extends Roo.bootstrap.Component
911  * Bootstrap ButtonGroup class
912  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913  * 
914  * @cfg {String} size lg | sm | xs (default empty normal)
915  * @cfg {String} align vertical | justified  (default none)
916  * @cfg {String} direction up | down (default down)
917  * @cfg {Boolean} toolbar false | true
918  * @cfg {Boolean} btn true | false
919  * 
920  * 
921  * @constructor
922  * Create a new Input
923  * @param {Object} config The config object
924  */
925
926 Roo.bootstrap.ButtonGroup = function(config){
927     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
928 };
929
930 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
931     
932     size: '',
933     align: '',
934     direction: '',
935     toolbar: false,
936     btn: true,
937
938     getAutoCreate : function(){
939         var cfg = {
940             cls: 'btn-group',
941             html : null
942         };
943         
944         cfg.html = this.html || cfg.html;
945         
946         if (this.toolbar) {
947             cfg = {
948                 cls: 'btn-toolbar',
949                 html: null
950             };
951             
952             return cfg;
953         }
954         
955         if (['vertical','justified'].indexOf(this.align)!==-1) {
956             cfg.cls = 'btn-group-' + this.align;
957             
958             if (this.align == 'justified') {
959                 console.log(this.items);
960             }
961         }
962         
963         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
964             cfg.cls += ' btn-group-' + this.size;
965         }
966         
967         if (this.direction == 'up') {
968             cfg.cls += ' dropup' ;
969         }
970         
971         return cfg;
972     },
973     /**
974      * Add a button to the group (similar to NavItem API.)
975      */
976     addItem : function(cfg)
977     {
978         var cn = new Roo.bootstrap.Button(cfg);
979         //this.register(cn);
980         cn.parentId = this.id;
981         cn.onRender(this.el, null);
982         return cn;
983     }
984    
985 });
986
987  /*
988  * - LGPL
989  *
990  * button
991  * 
992  */
993
994 /**
995  * @class Roo.bootstrap.Button
996  * @extends Roo.bootstrap.Component
997  * Bootstrap Button class
998  * @cfg {String} html The button content
999  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1000  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1001  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1002  * @cfg {String} size (lg|sm|xs)
1003  * @cfg {String} tag (a|input|submit)
1004  * @cfg {String} href empty or href
1005  * @cfg {Boolean} disabled default false;
1006  * @cfg {Boolean} isClose default false;
1007  * @cfg {String} glyphicon depricated - use fa
1008  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1009  * @cfg {String} badge text for badge
1010  * @cfg {String} theme (default|glow)  
1011  * @cfg {Boolean} inverse dark themed version
1012  * @cfg {Boolean} toggle is it a slidy toggle button
1013  * @cfg {Boolean} pressed   default null - if the button ahs active state
1014  * @cfg {String} ontext text for on slidy toggle state
1015  * @cfg {String} offtext text for off slidy toggle state
1016  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1017  * @cfg {Boolean} removeClass remove the standard class..
1018  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1019  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1020  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1021
1022  * @constructor
1023  * Create a new button
1024  * @param {Object} config The config object
1025  */
1026
1027
1028 Roo.bootstrap.Button = function(config){
1029     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1030     
1031     this.addEvents({
1032         // raw events
1033         /**
1034          * @event click
1035          * When a button is pressed
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          */
1039         "click" : true,
1040         /**
1041          * @event dblclick
1042          * When a button is double clicked
1043          * @param {Roo.bootstrap.Button} btn
1044          * @param {Roo.EventObject} e
1045          */
1046         "dblclick" : true,
1047          /**
1048          * @event toggle
1049          * After the button has been toggles
1050          * @param {Roo.bootstrap.Button} btn
1051          * @param {Roo.EventObject} e
1052          * @param {boolean} pressed (also available as button.pressed)
1053          */
1054         "toggle" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1059     html: false,
1060     active: false,
1061     weight: '',
1062     badge_weight: '',
1063     outline : false,
1064     size: '',
1065     tag: 'button',
1066     href: '',
1067     disabled: false,
1068     isClose: false,
1069     glyphicon: '',
1070     fa: '',
1071     badge: '',
1072     theme: 'default',
1073     inverse: false,
1074     
1075     toggle: false,
1076     ontext: 'ON',
1077     offtext: 'OFF',
1078     defaulton: true,
1079     preventDefault: true,
1080     removeClass: false,
1081     name: false,
1082     target: false,
1083     group : false,
1084      
1085     pressed : null,
1086      
1087     
1088     getAutoCreate : function(){
1089         
1090         var cfg = {
1091             tag : 'button',
1092             cls : 'roo-button',
1093             html: ''
1094         };
1095         
1096         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1097             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1098             this.tag = 'button';
1099         } else {
1100             cfg.tag = this.tag;
1101         }
1102         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103         
1104         if (this.toggle == true) {
1105             cfg={
1106                 tag: 'div',
1107                 cls: 'slider-frame roo-button',
1108                 cn: [
1109                     {
1110                         tag: 'span',
1111                         'data-on-text':'ON',
1112                         'data-off-text':'OFF',
1113                         cls: 'slider-button',
1114                         html: this.offtext
1115                     }
1116                 ]
1117             };
1118             // why are we validating the weights?
1119             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1120                 cfg.cls +=  ' ' + this.weight;
1121             }
1122             
1123             return cfg;
1124         }
1125         
1126         if (this.isClose) {
1127             cfg.cls += ' close';
1128             
1129             cfg["aria-hidden"] = true;
1130             
1131             cfg.html = "&times;";
1132             
1133             return cfg;
1134         }
1135              
1136         
1137         if (this.theme==='default') {
1138             cfg.cls = 'btn roo-button';
1139             
1140             //if (this.parentType != 'Navbar') {
1141             this.weight = this.weight.length ?  this.weight : 'default';
1142             //}
1143             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144                 
1145                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1146                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1147                 cfg.cls += ' btn-' + outline + weight;
1148                 if (this.weight == 'default') {
1149                     // BC
1150                     cfg.cls += ' btn-' + this.weight;
1151                 }
1152             }
1153         } else if (this.theme==='glow') {
1154             
1155             cfg.tag = 'a';
1156             cfg.cls = 'btn-glow roo-button';
1157             
1158             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159                 
1160                 cfg.cls += ' ' + this.weight;
1161             }
1162         }
1163    
1164         
1165         if (this.inverse) {
1166             this.cls += ' inverse';
1167         }
1168         
1169         
1170         if (this.active || this.pressed === true) {
1171             cfg.cls += ' active';
1172         }
1173         
1174         if (this.disabled) {
1175             cfg.disabled = 'disabled';
1176         }
1177         
1178         if (this.items) {
1179             Roo.log('changing to ul' );
1180             cfg.tag = 'ul';
1181             this.glyphicon = 'caret';
1182             if (Roo.bootstrap.version == 4) {
1183                 this.fa = 'caret-down';
1184             }
1185             
1186         }
1187         
1188         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189          
1190         //gsRoo.log(this.parentType);
1191         if (this.parentType === 'Navbar' && !this.parent().bar) {
1192             Roo.log('changing to li?');
1193             
1194             cfg.tag = 'li';
1195             
1196             cfg.cls = '';
1197             cfg.cn =  [{
1198                 tag : 'a',
1199                 cls : 'roo-button',
1200                 html : this.html,
1201                 href : this.href || '#'
1202             }];
1203             if (this.menu) {
1204                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1205                 cfg.cls += ' dropdown';
1206             }   
1207             
1208             delete cfg.html;
1209             
1210         }
1211         
1212        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1213         
1214         if (this.glyphicon) {
1215             cfg.html = ' ' + cfg.html;
1216             
1217             cfg.cn = [
1218                 {
1219                     tag: 'span',
1220                     cls: 'glyphicon glyphicon-' + this.glyphicon
1221                 }
1222             ];
1223         }
1224         if (this.fa) {
1225             cfg.html = ' ' + cfg.html;
1226             
1227             cfg.cn = [
1228                 {
1229                     tag: 'i',
1230                     cls: 'fa fas fa-' + this.fa
1231                 }
1232             ];
1233         }
1234         
1235         if (this.badge) {
1236             cfg.html += ' ';
1237             
1238             cfg.tag = 'a';
1239             
1240 //            cfg.cls='btn roo-button';
1241             
1242             cfg.href=this.href;
1243             
1244             var value = cfg.html;
1245             
1246             if(this.glyphicon){
1247                 value = {
1248                     tag: 'span',
1249                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1250                     html: this.html
1251                 };
1252             }
1253             if(this.fa){
1254                 value = {
1255                     tag: 'i',
1256                     cls: 'fa fas fa-' + this.fa,
1257                     html: this.html
1258                 };
1259             }
1260             
1261             var bw = this.badge_weight.length ? this.badge_weight :
1262                 (this.weight.length ? this.weight : 'secondary');
1263             bw = bw == 'default' ? 'secondary' : bw;
1264             
1265             cfg.cn = [
1266                 value,
1267                 {
1268                     tag: 'span',
1269                     cls: 'badge badge-' + bw,
1270                     html: this.badge
1271                 }
1272             ];
1273             
1274             cfg.html='';
1275         }
1276         
1277         if (this.menu) {
1278             cfg.cls += ' dropdown';
1279             cfg.html = typeof(cfg.html) != 'undefined' ?
1280                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1281         }
1282         
1283         if (cfg.tag !== 'a' && this.href !== '') {
1284             throw "Tag must be a to set href.";
1285         } else if (this.href.length > 0) {
1286             cfg.href = this.href;
1287         }
1288         
1289         if(this.removeClass){
1290             cfg.cls = '';
1291         }
1292         
1293         if(this.target){
1294             cfg.target = this.target;
1295         }
1296         
1297         return cfg;
1298     },
1299     initEvents: function() {
1300        // Roo.log('init events?');
1301 //        Roo.log(this.el.dom);
1302         // add the menu...
1303         
1304         if (typeof (this.menu) != 'undefined') {
1305             this.menu.parentType = this.xtype;
1306             this.menu.triggerEl = this.el;
1307             this.addxtype(Roo.apply({}, this.menu));
1308         }
1309
1310
1311         if (this.el.hasClass('roo-button')) {
1312              this.el.on('click', this.onClick, this);
1313              this.el.on('dblclick', this.onDblClick, this);
1314         } else {
1315              this.el.select('.roo-button').on('click', this.onClick, this);
1316              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1317              
1318         }
1319         // why?
1320         if(this.removeClass){
1321             this.el.on('click', this.onClick, this);
1322         }
1323         
1324         if (this.group === true) {
1325              if (this.pressed === false || this.pressed === true) {
1326                 // nothing
1327             } else {
1328                 this.pressed = false;
1329                 this.setActive(this.pressed);
1330             }
1331             
1332         }
1333         
1334         this.el.enableDisplayMode();
1335         
1336     },
1337     onClick : function(e)
1338     {
1339         if (this.disabled) {
1340             return;
1341         }
1342         
1343         Roo.log('button on click ');
1344         if(this.preventDefault){
1345             e.preventDefault();
1346         }
1347         
1348         if (this.group) {
1349             if (this.pressed) {
1350                 // do nothing -
1351                 return;
1352             }
1353             this.setActive(true);
1354             var pi = this.parent().items;
1355             for (var i = 0;i < pi.length;i++) {
1356                 if (this == pi[i]) {
1357                     continue;
1358                 }
1359                 if (pi[i].el.hasClass('roo-button')) {
1360                     pi[i].setActive(false);
1361                 }
1362             }
1363             this.fireEvent('click', this, e);            
1364             return;
1365         }
1366         
1367         if (this.pressed === true || this.pressed === false) {
1368             this.toggleActive(e);
1369         }
1370         
1371         
1372         this.fireEvent('click', this, e);
1373     },
1374     onDblClick: function(e)
1375     {
1376         if (this.disabled) {
1377             return;
1378         }
1379         if(this.preventDefault){
1380             e.preventDefault();
1381         }
1382         this.fireEvent('dblclick', this, e);
1383     },
1384     /**
1385      * Enables this button
1386      */
1387     enable : function()
1388     {
1389         this.disabled = false;
1390         this.el.removeClass('disabled');
1391         this.el.dom.removeAttribute("disabled");
1392     },
1393     
1394     /**
1395      * Disable this button
1396      */
1397     disable : function()
1398     {
1399         this.disabled = true;
1400         this.el.addClass('disabled');
1401         this.el.attr("disabled", "disabled")
1402     },
1403      /**
1404      * sets the active state on/off, 
1405      * @param {Boolean} state (optional) Force a particular state
1406      */
1407     setActive : function(v) {
1408         
1409         this.el[v ? 'addClass' : 'removeClass']('active');
1410         this.pressed = v;
1411     },
1412      /**
1413      * toggles the current active state 
1414      */
1415     toggleActive : function(e)
1416     {
1417         this.setActive(!this.pressed); // this modifies pressed...
1418         this.fireEvent('toggle', this, e, this.pressed);
1419     },
1420      /**
1421      * get the current active state
1422      * @return {boolean} true if it's active
1423      */
1424     isActive : function()
1425     {
1426         return this.el.hasClass('active');
1427     },
1428     /**
1429      * set the text of the first selected button
1430      */
1431     setText : function(str)
1432     {
1433         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1434     },
1435     /**
1436      * get the text of the first selected button
1437      */
1438     getText : function()
1439     {
1440         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1441     },
1442     
1443     setWeight : function(str)
1444     {
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1446         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447         this.weight = str;
1448         var outline = this.outline ? 'outline-' : '';
1449         if (str == 'default') {
1450             this.el.addClass('btn-default btn-outline-secondary');        
1451             return;
1452         }
1453         this.el.addClass('btn-' + outline + str);        
1454     }
1455     
1456     
1457 });
1458 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459
1460 Roo.bootstrap.Button.weights = [
1461     'default',
1462     'secondary' ,
1463     'primary',
1464     'success',
1465     'info',
1466     'warning',
1467     'danger',
1468     'link',
1469     'light',
1470     'dark'              
1471    
1472 ];/*
1473  * - LGPL
1474  *
1475  * column
1476  * 
1477  */
1478
1479 /**
1480  * @class Roo.bootstrap.Column
1481  * @extends Roo.bootstrap.Component
1482  * @children Roo.bootstrap.Component
1483  * Bootstrap Column class
1484  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1485  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1486  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1487  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1488  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1489  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1490  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1491  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1492  *
1493  * 
1494  * @cfg {Boolean} hidden (true|false) hide the element
1495  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1496  * @cfg {String} fa (ban|check|...) font awesome icon
1497  * @cfg {Number} fasize (1|2|....) font awsome size
1498
1499  * @cfg {String} icon (info-sign|check|...) glyphicon name
1500
1501  * @cfg {String} html content of column.
1502  * 
1503  * @constructor
1504  * Create a new Column
1505  * @param {Object} config The config object
1506  */
1507
1508 Roo.bootstrap.Column = function(config){
1509     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1510 };
1511
1512 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1513     
1514     xs: false,
1515     sm: false,
1516     md: false,
1517     lg: false,
1518     xsoff: false,
1519     smoff: false,
1520     mdoff: false,
1521     lgoff: false,
1522     html: '',
1523     offset: 0,
1524     alert: false,
1525     fa: false,
1526     icon : false,
1527     hidden : false,
1528     fasize : 1,
1529     
1530     getAutoCreate : function(){
1531         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1532         
1533         cfg = {
1534             tag: 'div',
1535             cls: 'column'
1536         };
1537         
1538         var settings=this;
1539         var sizes =   ['xs','sm','md','lg'];
1540         sizes.map(function(size ,ix){
1541             //Roo.log( size + ':' + settings[size]);
1542             
1543             if (settings[size+'off'] !== false) {
1544                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1545             }
1546             
1547             if (settings[size] === false) {
1548                 return;
1549             }
1550             
1551             if (!settings[size]) { // 0 = hidden
1552                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553                 // bootsrap4
1554                 for (var i = ix; i > -1; i--) {
1555                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1556                 }
1557                 
1558                 
1559                 return;
1560             }
1561             cfg.cls += ' col-' + size + '-' + settings[size] + (
1562                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1563             );
1564             
1565         });
1566         
1567         if (this.hidden) {
1568             cfg.cls += ' hidden';
1569         }
1570         
1571         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1572             cfg.cls +=' alert alert-' + this.alert;
1573         }
1574         
1575         
1576         if (this.html.length) {
1577             cfg.html = this.html;
1578         }
1579         if (this.fa) {
1580             var fasize = '';
1581             if (this.fasize > 1) {
1582                 fasize = ' fa-' + this.fasize + 'x';
1583             }
1584             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1585             
1586             
1587         }
1588         if (this.icon) {
1589             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1590         }
1591         
1592         return cfg;
1593     }
1594    
1595 });
1596
1597  
1598
1599  /*
1600  * - LGPL
1601  *
1602  * page container.
1603  * 
1604  */
1605
1606
1607 /**
1608  * @class Roo.bootstrap.Container
1609  * @extends Roo.bootstrap.Component
1610  * @builder-top
1611  * @children Roo.bootstrap.Component
1612  * Bootstrap Container class
1613  * @cfg {Boolean} jumbotron is it a jumbotron element
1614  * @cfg {String} html content of element
1615  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1616  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1617  * @cfg {String} header content of header (for panel)
1618  * @cfg {String} footer content of footer (for panel)
1619  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1620  * @cfg {String} tag (header|aside|section) type of HTML tag.
1621  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1622  * @cfg {String} fa font awesome icon
1623  * @cfg {String} icon (info-sign|check|...) glyphicon name
1624  * @cfg {Boolean} hidden (true|false) hide the element
1625  * @cfg {Boolean} expandable (true|false) default false
1626  * @cfg {Boolean} expanded (true|false) default true
1627  * @cfg {String} rheader contet on the right of header
1628  * @cfg {Boolean} clickable (true|false) default false
1629
1630  *     
1631  * @constructor
1632  * Create a new Container
1633  * @param {Object} config The config object
1634  */
1635
1636 Roo.bootstrap.Container = function(config){
1637     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1638     
1639     this.addEvents({
1640         // raw events
1641          /**
1642          * @event expand
1643          * After the panel has been expand
1644          * 
1645          * @param {Roo.bootstrap.Container} this
1646          */
1647         "expand" : true,
1648         /**
1649          * @event collapse
1650          * After the panel has been collapsed
1651          * 
1652          * @param {Roo.bootstrap.Container} this
1653          */
1654         "collapse" : true,
1655         /**
1656          * @event click
1657          * When a element is chick
1658          * @param {Roo.bootstrap.Container} this
1659          * @param {Roo.EventObject} e
1660          */
1661         "click" : true
1662     });
1663 };
1664
1665 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1666     
1667     jumbotron : false,
1668     well: '',
1669     panel : '',
1670     header: '',
1671     footer : '',
1672     sticky: '',
1673     tag : false,
1674     alert : false,
1675     fa: false,
1676     icon : false,
1677     expandable : false,
1678     rheader : '',
1679     expanded : true,
1680     clickable: false,
1681   
1682      
1683     getChildContainer : function() {
1684         
1685         if(!this.el){
1686             return false;
1687         }
1688         
1689         if (this.panel.length) {
1690             return this.el.select('.panel-body',true).first();
1691         }
1692         
1693         return this.el;
1694     },
1695     
1696     
1697     getAutoCreate : function(){
1698         
1699         var cfg = {
1700             tag : this.tag || 'div',
1701             html : '',
1702             cls : ''
1703         };
1704         if (this.jumbotron) {
1705             cfg.cls = 'jumbotron';
1706         }
1707         
1708         
1709         
1710         // - this is applied by the parent..
1711         //if (this.cls) {
1712         //    cfg.cls = this.cls + '';
1713         //}
1714         
1715         if (this.sticky.length) {
1716             
1717             var bd = Roo.get(document.body);
1718             if (!bd.hasClass('bootstrap-sticky')) {
1719                 bd.addClass('bootstrap-sticky');
1720                 Roo.select('html',true).setStyle('height', '100%');
1721             }
1722              
1723             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1724         }
1725         
1726         
1727         if (this.well.length) {
1728             switch (this.well) {
1729                 case 'lg':
1730                 case 'sm':
1731                     cfg.cls +=' well well-' +this.well;
1732                     break;
1733                 default:
1734                     cfg.cls +=' well';
1735                     break;
1736             }
1737         }
1738         
1739         if (this.hidden) {
1740             cfg.cls += ' hidden';
1741         }
1742         
1743         
1744         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1745             cfg.cls +=' alert alert-' + this.alert;
1746         }
1747         
1748         var body = cfg;
1749         
1750         if (this.panel.length) {
1751             cfg.cls += ' panel panel-' + this.panel;
1752             cfg.cn = [];
1753             if (this.header.length) {
1754                 
1755                 var h = [];
1756                 
1757                 if(this.expandable){
1758                     
1759                     cfg.cls = cfg.cls + ' expandable';
1760                     
1761                     h.push({
1762                         tag: 'i',
1763                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1764                     });
1765                     
1766                 }
1767                 
1768                 h.push(
1769                     {
1770                         tag: 'span',
1771                         cls : 'panel-title',
1772                         html : (this.expandable ? '&nbsp;' : '') + this.header
1773                     },
1774                     {
1775                         tag: 'span',
1776                         cls: 'panel-header-right',
1777                         html: this.rheader
1778                     }
1779                 );
1780                 
1781                 cfg.cn.push({
1782                     cls : 'panel-heading',
1783                     style : this.expandable ? 'cursor: pointer' : '',
1784                     cn : h
1785                 });
1786                 
1787             }
1788             
1789             body = false;
1790             cfg.cn.push({
1791                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1792                 html : this.html
1793             });
1794             
1795             
1796             if (this.footer.length) {
1797                 cfg.cn.push({
1798                     cls : 'panel-footer',
1799                     html : this.footer
1800                     
1801                 });
1802             }
1803             
1804         }
1805         
1806         if (body) {
1807             body.html = this.html || cfg.html;
1808             // prefix with the icons..
1809             if (this.fa) {
1810                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1811             }
1812             if (this.icon) {
1813                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1814             }
1815             
1816             
1817         }
1818         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1819             cfg.cls =  'container';
1820         }
1821         
1822         return cfg;
1823     },
1824     
1825     initEvents: function() 
1826     {
1827         if(this.expandable){
1828             var headerEl = this.headerEl();
1829         
1830             if(headerEl){
1831                 headerEl.on('click', this.onToggleClick, this);
1832             }
1833         }
1834         
1835         if(this.clickable){
1836             this.el.on('click', this.onClick, this);
1837         }
1838         
1839     },
1840     
1841     onToggleClick : function()
1842     {
1843         var headerEl = this.headerEl();
1844         
1845         if(!headerEl){
1846             return;
1847         }
1848         
1849         if(this.expanded){
1850             this.collapse();
1851             return;
1852         }
1853         
1854         this.expand();
1855     },
1856     
1857     expand : function()
1858     {
1859         if(this.fireEvent('expand', this)) {
1860             
1861             this.expanded = true;
1862             
1863             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864             
1865             this.el.select('.panel-body',true).first().removeClass('hide');
1866             
1867             var toggleEl = this.toggleEl();
1868
1869             if(!toggleEl){
1870                 return;
1871             }
1872
1873             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1874         }
1875         
1876     },
1877     
1878     collapse : function()
1879     {
1880         if(this.fireEvent('collapse', this)) {
1881             
1882             this.expanded = false;
1883             
1884             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1885             this.el.select('.panel-body',true).first().addClass('hide');
1886         
1887             var toggleEl = this.toggleEl();
1888
1889             if(!toggleEl){
1890                 return;
1891             }
1892
1893             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1894         }
1895     },
1896     
1897     toggleEl : function()
1898     {
1899         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1900             return;
1901         }
1902         
1903         return this.el.select('.panel-heading .fa',true).first();
1904     },
1905     
1906     headerEl : function()
1907     {
1908         if(!this.el || !this.panel.length || !this.header.length){
1909             return;
1910         }
1911         
1912         return this.el.select('.panel-heading',true).first()
1913     },
1914     
1915     bodyEl : function()
1916     {
1917         if(!this.el || !this.panel.length){
1918             return;
1919         }
1920         
1921         return this.el.select('.panel-body',true).first()
1922     },
1923     
1924     titleEl : function()
1925     {
1926         if(!this.el || !this.panel.length || !this.header.length){
1927             return;
1928         }
1929         
1930         return this.el.select('.panel-title',true).first();
1931     },
1932     
1933     setTitle : function(v)
1934     {
1935         var titleEl = this.titleEl();
1936         
1937         if(!titleEl){
1938             return;
1939         }
1940         
1941         titleEl.dom.innerHTML = v;
1942     },
1943     
1944     getTitle : function()
1945     {
1946         
1947         var titleEl = this.titleEl();
1948         
1949         if(!titleEl){
1950             return '';
1951         }
1952         
1953         return titleEl.dom.innerHTML;
1954     },
1955     
1956     setRightTitle : function(v)
1957     {
1958         var t = this.el.select('.panel-header-right',true).first();
1959         
1960         if(!t){
1961             return;
1962         }
1963         
1964         t.dom.innerHTML = v;
1965     },
1966     
1967     onClick : function(e)
1968     {
1969         e.preventDefault();
1970         
1971         this.fireEvent('click', this, e);
1972     }
1973 });
1974
1975  /**
1976  * @class Roo.bootstrap.Card
1977  * @extends Roo.bootstrap.Component
1978  * @children Roo.bootstrap.Component
1979  * @licence LGPL
1980  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1981  *
1982  *
1983  * possible... may not be implemented..
1984  * @cfg {String} header_image  src url of image.
1985  * @cfg {String|Object} header
1986  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1987  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1988  * 
1989  * @cfg {String} title
1990  * @cfg {String} subtitle
1991  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1992  * @cfg {String} footer
1993  
1994  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995  * 
1996  * @cfg {String} margin (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2002  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003  *
2004  * @cfg {String} padding (0|1|2|3|4|5)
2005  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2006  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2007  * @cfg {String} padding_left (0|1|2|3|4|5)
2008  * @cfg {String} padding_right (0|1|2|3|4|5)
2009  * @cfg {String} padding_x (0|1|2|3|4|5)
2010  * @cfg {String} padding_y (0|1|2|3|4|5)
2011  *
2012  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017  
2018  * @config {Boolean} dragable  if this card can be dragged.
2019  * @config {String} drag_group  group for drag
2020  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2021  * @config {String} drop_group  group for drag
2022  * 
2023  * @config {Boolean} collapsable can the body be collapsed.
2024  * @config {Boolean} collapsed is the body collapsed when rendered...
2025  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2026  * @config {Boolean} rotated is the body rotated when rendered...
2027  * 
2028  * @constructor
2029  * Create a new Container
2030  * @param {Object} config The config object
2031  */
2032
2033 Roo.bootstrap.Card = function(config){
2034     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2035     
2036     this.addEvents({
2037          // raw events
2038         /**
2039          * @event drop
2040          * When a element a card is dropped
2041          * @param {Roo.bootstrap.Card} this
2042          *
2043          * 
2044          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2045          * @param {String} position 'above' or 'below'
2046          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2047         
2048          */
2049         'drop' : true,
2050          /**
2051          * @event rotate
2052          * When a element a card is rotate
2053          * @param {Roo.bootstrap.Card} this
2054          * @param {Roo.Element} n the node being dropped?
2055          * @param {Boolean} rotate status
2056          */
2057         'rotate' : true,
2058         /**
2059          * @event cardover
2060          * When a card element is dragged over ready to drop (return false to block dropable)
2061          * @param {Roo.bootstrap.Card} this
2062          * @param {Object} data from dragdrop 
2063          */
2064          'cardover' : true
2065          
2066     });
2067 };
2068
2069
2070 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2071     
2072     
2073     weight : '',
2074     
2075     margin: '', /// may be better in component?
2076     margin_top: '', 
2077     margin_bottom: '', 
2078     margin_left: '',
2079     margin_right: '',
2080     margin_x: '',
2081     margin_y: '',
2082     
2083     padding : '',
2084     padding_top: '', 
2085     padding_bottom: '', 
2086     padding_left: '',
2087     padding_right: '',
2088     padding_x: '',
2089     padding_y: '',
2090     
2091     display: '', 
2092     display_xs: '', 
2093     display_sm: '', 
2094     display_lg: '',
2095     display_xl: '',
2096  
2097     header_image  : '',
2098     header : '',
2099     header_size : 0,
2100     title : '',
2101     subtitle : '',
2102     html : '',
2103     footer: '',
2104
2105     collapsable : false,
2106     collapsed : false,
2107     rotateable : false,
2108     rotated : false,
2109     
2110     dragable : false,
2111     drag_group : false,
2112     dropable : false,
2113     drop_group : false,
2114     childContainer : false,
2115     dropEl : false, /// the dom placeholde element that indicates drop location.
2116     containerEl: false, // body container
2117     bodyEl: false, // card-body
2118     headerContainerEl : false, //
2119     headerEl : false,
2120     header_imageEl : false,
2121     
2122     
2123     layoutCls : function()
2124     {
2125         var cls = '';
2126         var t = this;
2127         Roo.log(this.margin_bottom.length);
2128         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2129             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130             
2131             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2132                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2133             }
2134             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2135                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2136             }
2137         });
2138         
2139         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2140             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2141                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2142             }
2143         });
2144         
2145         // more generic support?
2146         if (this.hidden) {
2147             cls += ' d-none';
2148         }
2149         
2150         return cls;
2151     },
2152  
2153        // Roo.log("Call onRender: " + this.xtype);
2154         /*  We are looking at something like this.
2155 <div class="card">
2156     <img src="..." class="card-img-top" alt="...">
2157     <div class="card-body">
2158         <h5 class="card-title">Card title</h5>
2159          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160
2161         >> this bit is really the body...
2162         <div> << we will ad dthis in hopefully it will not break shit.
2163         
2164         ** card text does not actually have any styling...
2165         
2166             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2167         
2168         </div> <<
2169           <a href="#" class="card-link">Card link</a>
2170           
2171     </div>
2172     <div class="card-footer">
2173         <small class="text-muted">Last updated 3 mins ago</small>
2174     </div>
2175 </div>
2176          */
2177     getAutoCreate : function(){
2178         
2179         var cfg = {
2180             tag : 'div',
2181             cls : 'card',
2182             cn : [ ]
2183         };
2184         
2185         if (this.weight.length && this.weight != 'light') {
2186             cfg.cls += ' text-white';
2187         } else {
2188             cfg.cls += ' text-dark'; // need as it's nested..
2189         }
2190         if (this.weight.length) {
2191             cfg.cls += ' bg-' + this.weight;
2192         }
2193         
2194         cfg.cls += ' ' + this.layoutCls(); 
2195         
2196         var hdr = false;
2197         var hdr_ctr = false;
2198         if (this.header.length) {
2199             hdr = {
2200                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2201                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202                 cn : []
2203             };
2204             cfg.cn.push(hdr);
2205             hdr_ctr = hdr;
2206         } else {
2207             hdr = {
2208                 tag : 'div',
2209                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2210                 cn : []
2211             };
2212             cfg.cn.push(hdr);
2213             hdr_ctr = hdr;
2214         }
2215         if (this.collapsable) {
2216             hdr_ctr = {
2217             tag : 'a',
2218             cls : 'd-block user-select-none',
2219             cn: [
2220                     {
2221                         tag: 'i',
2222                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2223                     }
2224                    
2225                 ]
2226             };
2227             hdr.cn.push(hdr_ctr);
2228         }
2229         
2230         hdr_ctr.cn.push(        {
2231             tag: 'span',
2232             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2233             html : this.header
2234         });
2235         
2236         
2237         if (this.header_image.length) {
2238             cfg.cn.push({
2239                 tag : 'img',
2240                 cls : 'card-img-top',
2241                 src: this.header_image // escape?
2242             });
2243         } else {
2244             cfg.cn.push({
2245                     tag : 'div',
2246                     cls : 'card-img-top d-none' 
2247                 });
2248         }
2249             
2250         var body = {
2251             tag : 'div',
2252             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2253             cn : []
2254         };
2255         var obody = body;
2256         if (this.collapsable || this.rotateable) {
2257             obody = {
2258                 tag: 'div',
2259                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2260                 cn : [  body ]
2261             };
2262         }
2263         
2264         cfg.cn.push(obody);
2265         
2266         if (this.title.length) {
2267             body.cn.push({
2268                 tag : 'div',
2269                 cls : 'card-title',
2270                 src: this.title // escape?
2271             });
2272         }  
2273         
2274         if (this.subtitle.length) {
2275             body.cn.push({
2276                 tag : 'div',
2277                 cls : 'card-title',
2278                 src: this.subtitle // escape?
2279             });
2280         }
2281         
2282         body.cn.push({
2283             tag : 'div',
2284             cls : 'roo-card-body-ctr'
2285         });
2286         
2287         if (this.html.length) {
2288             body.cn.push({
2289                 tag: 'div',
2290                 html : this.html
2291             });
2292         }
2293         // fixme ? handle objects?
2294         
2295         if (this.footer.length) {
2296            
2297             cfg.cn.push({
2298                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2299                 html : this.footer
2300             });
2301             
2302         } else {
2303             cfg.cn.push({cls : 'card-footer d-none'});
2304         }
2305         
2306         // footer...
2307         
2308         return cfg;
2309     },
2310     
2311     
2312     getCardHeader : function()
2313     {
2314         var  ret = this.el.select('.card-header',true).first();
2315         if (ret.hasClass('d-none')) {
2316             ret.removeClass('d-none');
2317         }
2318         
2319         return ret;
2320     },
2321     getCardFooter : function()
2322     {
2323         var  ret = this.el.select('.card-footer',true).first();
2324         if (ret.hasClass('d-none')) {
2325             ret.removeClass('d-none');
2326         }
2327         
2328         return ret;
2329     },
2330     getCardImageTop : function()
2331     {
2332         var  ret = this.header_imageEl;
2333         if (ret.hasClass('d-none')) {
2334             ret.removeClass('d-none');
2335         }
2336             
2337         return ret;
2338     },
2339     
2340     getChildContainer : function()
2341     {
2342         
2343         if(!this.el){
2344             return false;
2345         }
2346         return this.el.select('.roo-card-body-ctr',true).first();    
2347     },
2348     
2349     initEvents: function() 
2350     {
2351         this.bodyEl = this.el.select('.card-body',true).first(); 
2352         this.containerEl = this.getChildContainer();
2353         if(this.dragable){
2354             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2355                     containerScroll: true,
2356                     ddGroup: this.drag_group || 'default_card_drag_group'
2357             });
2358             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359         }
2360         if (this.dropable) {
2361             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2362                 containerScroll: true,
2363                 ddGroup: this.drop_group || 'default_card_drag_group'
2364             });
2365             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2366             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2367             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2368             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2369             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2370         }
2371         
2372         if (this.collapsable) {
2373             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374         }
2375         if (this.rotateable) {
2376             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377         }
2378         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379          
2380         this.footerEl = this.el.select('.card-footer',true).first();
2381         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2382         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2383         this.headerEl = this.el.select('.card-header',true).first();
2384         
2385         if (this.rotated) {
2386             this.el.addClass('roo-card-rotated');
2387             this.fireEvent('rotate', this, true);
2388         }
2389         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2390         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2391         
2392     },
2393     getDragData : function(e)
2394     {
2395         var target = this.getEl();
2396         if (target) {
2397             //this.handleSelection(e);
2398             
2399             var dragData = {
2400                 source: this,
2401                 copy: false,
2402                 nodes: this.getEl(),
2403                 records: []
2404             };
2405             
2406             
2407             dragData.ddel = target.dom ;    // the div element
2408             Roo.log(target.getWidth( ));
2409             dragData.ddel.style.width = target.getWidth() + 'px';
2410             
2411             return dragData;
2412         }
2413         return false;
2414     },
2415     /**
2416     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2417     *    whole Element becomes the target, and this causes the drop gesture to append.
2418     *
2419     *    Returns an object:
2420     *     {
2421            
2422            position : 'below' or 'above'
2423            card  : relateive to card OBJECT (or true for no cards listed)
2424            items_n : relative to nth item in list
2425            card_n : relative to  nth card in list
2426     }
2427     *
2428     *    
2429     */
2430     getTargetFromEvent : function(e, dragged_card_el)
2431     {
2432         var target = e.getTarget();
2433         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2434             target = target.parentNode;
2435         }
2436         
2437         var ret = {
2438             position: '',
2439             cards : [],
2440             card_n : -1,
2441             items_n : -1,
2442             card : false 
2443         };
2444         
2445         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2446         // see if target is one of the 'cards'...
2447         
2448         
2449         //Roo.log(this.items.length);
2450         var pos = false;
2451         
2452         var last_card_n = 0;
2453         var cards_len  = 0;
2454         for (var i = 0;i< this.items.length;i++) {
2455             
2456             if (!this.items[i].el.hasClass('card')) {
2457                  continue;
2458             }
2459             pos = this.getDropPoint(e, this.items[i].el.dom);
2460             
2461             cards_len = ret.cards.length;
2462             //Roo.log(this.items[i].el.dom.id);
2463             ret.cards.push(this.items[i]);
2464             last_card_n  = i;
2465             if (ret.card_n < 0 && pos == 'above') {
2466                 ret.position = cards_len > 0 ? 'below' : pos;
2467                 ret.items_n = i > 0 ? i - 1 : 0;
2468                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2469                 ret.card = ret.cards[ret.card_n];
2470             }
2471         }
2472         if (!ret.cards.length) {
2473             ret.card = true;
2474             ret.position = 'below';
2475             ret.items_n;
2476             return ret;
2477         }
2478         // could not find a card.. stick it at the end..
2479         if (ret.card_n < 0) {
2480             ret.card_n = last_card_n;
2481             ret.card = ret.cards[last_card_n];
2482             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2483             ret.position = 'below';
2484         }
2485         
2486         if (this.items[ret.items_n].el == dragged_card_el) {
2487             return false;
2488         }
2489         
2490         if (ret.position == 'below') {
2491             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492             
2493             if (card_after  && card_after.el == dragged_card_el) {
2494                 return false;
2495             }
2496             return ret;
2497         }
2498         
2499         // its's after ..
2500         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501         
2502         if (card_before  && card_before.el == dragged_card_el) {
2503             return false;
2504         }
2505         
2506         return ret;
2507     },
2508     
2509     onNodeEnter : function(n, dd, e, data){
2510         return false;
2511     },
2512     onNodeOver : function(n, dd, e, data)
2513     {
2514        
2515         var target_info = this.getTargetFromEvent(e,data.source.el);
2516         if (target_info === false) {
2517             this.dropPlaceHolder('hide');
2518             return false;
2519         }
2520         Roo.log(['getTargetFromEvent', target_info ]);
2521         
2522         
2523         if (this.fireEvent('cardover', this, [ data ]) === false) {
2524             return false;
2525         }
2526         
2527         this.dropPlaceHolder('show', target_info,data);
2528         
2529         return false; 
2530     },
2531     onNodeOut : function(n, dd, e, data){
2532         this.dropPlaceHolder('hide');
2533      
2534     },
2535     onNodeDrop : function(n, dd, e, data)
2536     {
2537         
2538         // call drop - return false if
2539         
2540         // this could actually fail - if the Network drops..
2541         // we will ignore this at present..- client should probably reload
2542         // the whole set of cards if stuff like that fails.
2543         
2544         
2545         var info = this.getTargetFromEvent(e,data.source.el);
2546         if (info === false) {
2547             return false;
2548         }
2549         this.dropPlaceHolder('hide');
2550   
2551           
2552     
2553         this.acceptCard(data.source, info.position, info.card, info.items_n);
2554         return true;
2555          
2556     },
2557     firstChildCard : function()
2558     {
2559         for (var i = 0;i< this.items.length;i++) {
2560             
2561             if (!this.items[i].el.hasClass('card')) {
2562                  continue;
2563             }
2564             return this.items[i];
2565         }
2566         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2567     },
2568     /**
2569      * accept card
2570      *
2571      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2572      */
2573     acceptCard : function(move_card,  position, next_to_card )
2574     {
2575         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2576             return false;
2577         }
2578         
2579         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580         
2581         move_card.parent().removeCard(move_card);
2582         
2583         
2584         var dom = move_card.el.dom;
2585         dom.style.width = ''; // clear with - which is set by drag.
2586         
2587         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2588             var cardel = next_to_card.el.dom;
2589             
2590             if (position == 'above' ) {
2591                 cardel.parentNode.insertBefore(dom, cardel);
2592             } else if (cardel.nextSibling) {
2593                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594             } else {
2595                 cardel.parentNode.append(dom);
2596             }
2597         } else {
2598             // card container???
2599             this.containerEl.dom.append(dom);
2600         }
2601         
2602         //FIXME HANDLE card = true 
2603         
2604         // add this to the correct place in items.
2605         
2606         // remove Card from items.
2607         
2608        
2609         if (this.items.length) {
2610             var nitems = [];
2611             //Roo.log([info.items_n, info.position, this.items.length]);
2612             for (var i =0; i < this.items.length; i++) {
2613                 if (i == to_items_n && position == 'above') {
2614                     nitems.push(move_card);
2615                 }
2616                 nitems.push(this.items[i]);
2617                 if (i == to_items_n && position == 'below') {
2618                     nitems.push(move_card);
2619                 }
2620             }
2621             this.items = nitems;
2622             Roo.log(this.items);
2623         } else {
2624             this.items.push(move_card);
2625         }
2626         
2627         move_card.parentId = this.id;
2628         
2629         return true;
2630         
2631         
2632     },
2633     removeCard : function(c)
2634     {
2635         this.items = this.items.filter(function(e) { return e != c });
2636  
2637         var dom = c.el.dom;
2638         dom.parentNode.removeChild(dom);
2639         dom.style.width = ''; // clear with - which is set by drag.
2640         c.parentId = false;
2641         
2642     },
2643     
2644     /**    Decide whether to drop above or below a View node. */
2645     getDropPoint : function(e, n, dd)
2646     {
2647         if (dd) {
2648              return false;
2649         }
2650         if (n == this.containerEl.dom) {
2651             return "above";
2652         }
2653         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2654         var c = t + (b - t) / 2;
2655         var y = Roo.lib.Event.getPageY(e);
2656         if(y <= c) {
2657             return "above";
2658         }else{
2659             return "below";
2660         }
2661     },
2662     onToggleCollapse : function(e)
2663         {
2664         if (this.collapsed) {
2665             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2666             this.collapsableEl.addClass('show');
2667             this.collapsed = false;
2668             return;
2669         }
2670         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2671         this.collapsableEl.removeClass('show');
2672         this.collapsed = true;
2673         
2674     
2675     },
2676     
2677     onToggleRotate : function(e)
2678     {
2679         this.collapsableEl.removeClass('show');
2680         this.footerEl.removeClass('d-none');
2681         this.el.removeClass('roo-card-rotated');
2682         this.el.removeClass('d-none');
2683         if (this.rotated) {
2684             
2685             this.collapsableEl.addClass('show');
2686             this.rotated = false;
2687             this.fireEvent('rotate', this, this.rotated);
2688             return;
2689         }
2690         this.el.addClass('roo-card-rotated');
2691         this.footerEl.addClass('d-none');
2692         this.el.select('.roo-collapsable').removeClass('show');
2693         
2694         this.rotated = true;
2695         this.fireEvent('rotate', this, this.rotated);
2696     
2697     },
2698     
2699     dropPlaceHolder: function (action, info, data)
2700     {
2701         if (this.dropEl === false) {
2702             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2703             cls : 'd-none'
2704             },true);
2705         }
2706         this.dropEl.removeClass(['d-none', 'd-block']);        
2707         if (action == 'hide') {
2708             
2709             this.dropEl.addClass('d-none');
2710             return;
2711         }
2712         // FIXME - info.card == true!!!
2713         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714         
2715         if (info.card !== true) {
2716             var cardel = info.card.el.dom;
2717             
2718             if (info.position == 'above') {
2719                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2720             } else if (cardel.nextSibling) {
2721                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722             } else {
2723                 cardel.parentNode.append(this.dropEl.dom);
2724             }
2725         } else {
2726             // card container???
2727             this.containerEl.dom.append(this.dropEl.dom);
2728         }
2729         
2730         this.dropEl.addClass('d-block roo-card-dropzone');
2731         
2732         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2733         
2734         
2735     
2736     
2737     
2738     },
2739     setHeaderText: function(html)
2740     {
2741         this.header = html;
2742         if (this.headerContainerEl) {
2743             this.headerContainerEl.dom.innerHTML = html;
2744         }
2745     },
2746     onHeaderImageLoad : function(ev, he)
2747     {
2748         if (!this.header_image_fit_square) {
2749             return;
2750         }
2751         
2752         var hw = he.naturalHeight / he.naturalWidth;
2753         // wide image = < 0
2754         // tall image = > 1
2755         //var w = he.dom.naturalWidth;
2756         var ww = he.width;
2757         he.style.left =  0;
2758         he.style.position =  'relative';
2759         if (hw > 1) {
2760             var nw = (ww * (1/hw));
2761             Roo.get(he).setSize( ww * (1/hw),  ww);
2762             he.style.left =  ((ww - nw)/ 2) + 'px';
2763             he.style.position =  'relative';
2764         }
2765
2766     }
2767
2768     
2769 });
2770
2771 /*
2772  * - LGPL
2773  *
2774  * Card header - holder for the card header elements.
2775  * 
2776  */
2777
2778 /**
2779  * @class Roo.bootstrap.CardHeader
2780  * @extends Roo.bootstrap.Element
2781  * @parent Roo.bootstrap.Card
2782  * @children Roo.bootstrap.Component
2783  * Bootstrap CardHeader class
2784  * @constructor
2785  * Create a new Card Header - that you can embed children into
2786  * @param {Object} config The config object
2787  */
2788
2789 Roo.bootstrap.CardHeader = function(config){
2790     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2791 };
2792
2793 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2794     
2795     
2796     container_method : 'getCardHeader' 
2797     
2798      
2799     
2800     
2801    
2802 });
2803
2804  
2805
2806  /*
2807  * - LGPL
2808  *
2809  * Card footer - holder for the card footer elements.
2810  * 
2811  */
2812
2813 /**
2814  * @class Roo.bootstrap.CardFooter
2815  * @extends Roo.bootstrap.Element
2816  * @parent Roo.bootstrap.Card
2817  * @children Roo.bootstrap.Component
2818  * Bootstrap CardFooter class
2819  * 
2820  * @constructor
2821  * Create a new Card Footer - that you can embed children into
2822  * @param {Object} config The config object
2823  */
2824
2825 Roo.bootstrap.CardFooter = function(config){
2826     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2827 };
2828
2829 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2830     
2831     
2832     container_method : 'getCardFooter' 
2833     
2834      
2835     
2836     
2837    
2838 });
2839
2840  
2841
2842  /*
2843  * - LGPL
2844  *
2845  * Card header - holder for the card header elements.
2846  * 
2847  */
2848
2849 /**
2850  * @class Roo.bootstrap.CardImageTop
2851  * @extends Roo.bootstrap.Element
2852  * @parent Roo.bootstrap.Card
2853  * @children Roo.bootstrap.Component
2854  * Bootstrap CardImageTop class
2855  * 
2856  * @constructor
2857  * Create a new Card Image Top container
2858  * @param {Object} config The config object
2859  */
2860
2861 Roo.bootstrap.CardImageTop = function(config){
2862     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2863 };
2864
2865 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2866     
2867    
2868     container_method : 'getCardImageTop' 
2869     
2870      
2871     
2872    
2873 });
2874
2875  
2876
2877  
2878 /*
2879 * Licence: LGPL
2880 */
2881
2882 /**
2883  * @class Roo.bootstrap.ButtonUploader
2884  * @extends Roo.bootstrap.Button
2885  * Bootstrap Button Uploader class - it's a button which when you add files to it
2886  *
2887  * 
2888  * @cfg {Number} errorTimeout default 3000
2889  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2890  * @cfg {Array}  html The button text.
2891  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2892  *
2893  * @constructor
2894  * Create a new CardUploader
2895  * @param {Object} config The config object
2896  */
2897
2898 Roo.bootstrap.ButtonUploader = function(config){
2899     
2900  
2901     
2902     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2903     
2904      
2905      this.addEvents({
2906          // raw events
2907         /**
2908          * @event beforeselect
2909          * When button is pressed, before show upload files dialog is shown
2910          * @param {Roo.bootstrap.UploaderButton} this
2911          *
2912          */
2913         'beforeselect' : true,
2914          /**
2915          * @event fired when files have been selected, 
2916          * When a the download link is clicked
2917          * @param {Roo.bootstrap.UploaderButton} this
2918          * @param {Array} Array of files that have been uploaded
2919          */
2920         'uploaded' : true
2921         
2922     });
2923 };
2924  
2925 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2926     
2927      
2928     errorTimeout : 3000,
2929      
2930     images : false,
2931    
2932     fileCollection : false,
2933     allowBlank : true,
2934     
2935     multiple : true,
2936     
2937     getAutoCreate : function()
2938     {
2939         var im = {
2940             tag: 'input',
2941             type : 'file',
2942             cls : 'd-none  roo-card-upload-selector' 
2943           
2944         };
2945         if (this.multiple) {
2946             im.multiple = 'multiple';
2947         }
2948         
2949         return  {
2950             cls :'div' ,
2951             cn : [
2952                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2953                 im
2954
2955             ]
2956         };
2957            
2958          
2959     },
2960      
2961    
2962     initEvents : function()
2963     {
2964         
2965         Roo.bootstrap.Button.prototype.initEvents.call(this);
2966         
2967         
2968         
2969         
2970         
2971         this.urlAPI = (window.createObjectURL && window) || 
2972                                 (window.URL && URL.revokeObjectURL && URL) || 
2973                                 (window.webkitURL && webkitURL);
2974                         
2975          
2976          
2977          
2978         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2979         
2980         this.selectorEl.on('change', this.onFileSelected, this);
2981          
2982          
2983        
2984     },
2985     
2986    
2987     onClick : function(e)
2988     {
2989         e.preventDefault();
2990         
2991         if ( this.fireEvent('beforeselect', this) === false) {
2992             return;
2993         }
2994          
2995         this.selectorEl.dom.click();
2996          
2997     },
2998     
2999     onFileSelected : function(e)
3000     {
3001         e.preventDefault();
3002         
3003         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3004             return;
3005         }
3006         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3007         this.selectorEl.dom.value  = '';// hopefully reset..
3008         
3009         this.fireEvent('uploaded', this,  files );
3010         
3011     },
3012     
3013        
3014    
3015     
3016     /**
3017      * addCard - add an Attachment to the uploader
3018      * @param data - the data about the image to upload
3019      *
3020      * {
3021           id : 123
3022           title : "Title of file",
3023           is_uploaded : false,
3024           src : "http://.....",
3025           srcfile : { the File upload object },
3026           mimetype : file.type,
3027           preview : false,
3028           is_deleted : 0
3029           .. any other data...
3030         }
3031      *
3032      * 
3033     */
3034      
3035     reset: function()
3036     {
3037          
3038          this.selectorEl
3039     } 
3040     
3041     
3042     
3043     
3044 });
3045  /*
3046  * - LGPL
3047  *
3048  * image
3049  * 
3050  */
3051
3052
3053 /**
3054  * @class Roo.bootstrap.Img
3055  * @extends Roo.bootstrap.Component
3056  * Bootstrap Img class
3057  * @cfg {Boolean} imgResponsive false | true
3058  * @cfg {String} border rounded | circle | thumbnail
3059  * @cfg {String} src image source
3060  * @cfg {String} alt image alternative text
3061  * @cfg {String} href a tag href
3062  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3063  * @cfg {String} xsUrl xs image source
3064  * @cfg {String} smUrl sm image source
3065  * @cfg {String} mdUrl md image source
3066  * @cfg {String} lgUrl lg image source
3067  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3068  * 
3069  * @constructor
3070  * Create a new Input
3071  * @param {Object} config The config object
3072  */
3073
3074 Roo.bootstrap.Img = function(config){
3075     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3076     
3077     this.addEvents({
3078         // img events
3079         /**
3080          * @event click
3081          * The img click event for the img.
3082          * @param {Roo.EventObject} e
3083          */
3084         "click" : true,
3085         /**
3086          * @event load
3087          * The when any image loads
3088          * @param {Roo.EventObject} e
3089          */
3090         "load" : true
3091     });
3092 };
3093
3094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3095     
3096     imgResponsive: true,
3097     border: '',
3098     src: 'about:blank',
3099     href: false,
3100     target: false,
3101     xsUrl: '',
3102     smUrl: '',
3103     mdUrl: '',
3104     lgUrl: '',
3105     backgroundContain : false,
3106
3107     getAutoCreate : function()
3108     {   
3109         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3110             return this.createSingleImg();
3111         }
3112         
3113         var cfg = {
3114             tag: 'div',
3115             cls: 'roo-image-responsive-group',
3116             cn: []
3117         };
3118         var _this = this;
3119         
3120         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3121             
3122             if(!_this[size + 'Url']){
3123                 return;
3124             }
3125             
3126             var img = {
3127                 tag: 'img',
3128                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3129                 html: _this.html || cfg.html,
3130                 src: _this[size + 'Url']
3131             };
3132             
3133             img.cls += ' roo-image-responsive-' + size;
3134             
3135             var s = ['xs', 'sm', 'md', 'lg'];
3136             
3137             s.splice(s.indexOf(size), 1);
3138             
3139             Roo.each(s, function(ss){
3140                 img.cls += ' hidden-' + ss;
3141             });
3142             
3143             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3144                 cfg.cls += ' img-' + _this.border;
3145             }
3146             
3147             if(_this.alt){
3148                 cfg.alt = _this.alt;
3149             }
3150             
3151             if(_this.href){
3152                 var a = {
3153                     tag: 'a',
3154                     href: _this.href,
3155                     cn: [
3156                         img
3157                     ]
3158                 };
3159
3160                 if(this.target){
3161                     a.target = _this.target;
3162                 }
3163             }
3164             
3165             cfg.cn.push((_this.href) ? a : img);
3166             
3167         });
3168         
3169         return cfg;
3170     },
3171     
3172     createSingleImg : function()
3173     {
3174         var cfg = {
3175             tag: 'img',
3176             cls: (this.imgResponsive) ? 'img-responsive' : '',
3177             html : null,
3178             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3179         };
3180         
3181         if (this.backgroundContain) {
3182             cfg.cls += ' background-contain';
3183         }
3184         
3185         cfg.html = this.html || cfg.html;
3186         
3187         if (this.backgroundContain) {
3188             cfg.style="background-image: url(" + this.src + ')';
3189         } else {
3190             cfg.src = this.src || cfg.src;
3191         }
3192         
3193         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3194             cfg.cls += ' img-' + this.border;
3195         }
3196         
3197         if(this.alt){
3198             cfg.alt = this.alt;
3199         }
3200         
3201         if(this.href){
3202             var a = {
3203                 tag: 'a',
3204                 href: this.href,
3205                 cn: [
3206                     cfg
3207                 ]
3208             };
3209             
3210             if(this.target){
3211                 a.target = this.target;
3212             }
3213             
3214         }
3215         
3216         return (this.href) ? a : cfg;
3217     },
3218     
3219     initEvents: function() 
3220     {
3221         if(!this.href){
3222             this.el.on('click', this.onClick, this);
3223         }
3224         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3225             this.el.on('load', this.onImageLoad, this);
3226         } else {
3227             // not sure if this works.. not tested
3228             this.el.select('img', true).on('load', this.onImageLoad, this);
3229         }
3230         
3231     },
3232     
3233     onClick : function(e)
3234     {
3235         Roo.log('img onclick');
3236         this.fireEvent('click', this, e);
3237     },
3238     onImageLoad: function(e)
3239     {
3240         Roo.log('img load');
3241         this.fireEvent('load', this, e);
3242     },
3243     
3244     /**
3245      * Sets the url of the image - used to update it
3246      * @param {String} url the url of the image
3247      */
3248     
3249     setSrc : function(url)
3250     {
3251         this.src =  url;
3252         
3253         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3254             if (this.backgroundContain) {
3255                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3256             } else {
3257                 this.el.dom.src =  url;
3258             }
3259             return;
3260         }
3261         
3262         this.el.select('img', true).first().dom.src =  url;
3263     }
3264     
3265     
3266    
3267 });
3268
3269  /*
3270  * - LGPL
3271  *
3272  * image
3273  * 
3274  */
3275
3276
3277 /**
3278  * @class Roo.bootstrap.Link
3279  * @extends Roo.bootstrap.Component
3280  * @children Roo.bootstrap.Component
3281  * Bootstrap Link Class (eg. '<a href>')
3282  
3283  * @cfg {String} alt image alternative text
3284  * @cfg {String} href a tag href
3285  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3286  * @cfg {String} html the content of the link.
3287  * @cfg {String} anchor name for the anchor link
3288  * @cfg {String} fa - favicon
3289
3290  * @cfg {Boolean} preventDefault (true | false) default false
3291
3292  * 
3293  * @constructor
3294  * Create a new Input
3295  * @param {Object} config The config object
3296  */
3297
3298 Roo.bootstrap.Link = function(config){
3299     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3300     
3301     this.addEvents({
3302         // img events
3303         /**
3304          * @event click
3305          * The img click event for the img.
3306          * @param {Roo.EventObject} e
3307          */
3308         "click" : true
3309     });
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3313     
3314     href: false,
3315     target: false,
3316     preventDefault: false,
3317     anchor : false,
3318     alt : false,
3319     fa: false,
3320
3321
3322     getAutoCreate : function()
3323     {
3324         var html = this.html || '';
3325         
3326         if (this.fa !== false) {
3327             html = '<i class="fa fa-' + this.fa + '"></i>';
3328         }
3329         var cfg = {
3330             tag: 'a'
3331         };
3332         // anchor's do not require html/href...
3333         if (this.anchor === false) {
3334             cfg.html = html;
3335             cfg.href = this.href || '#';
3336         } else {
3337             cfg.name = this.anchor;
3338             if (this.html !== false || this.fa !== false) {
3339                 cfg.html = html;
3340             }
3341             if (this.href !== false) {
3342                 cfg.href = this.href;
3343             }
3344         }
3345         
3346         if(this.alt !== false){
3347             cfg.alt = this.alt;
3348         }
3349         
3350         
3351         if(this.target !== false) {
3352             cfg.target = this.target;
3353         }
3354         
3355         return cfg;
3356     },
3357     
3358     initEvents: function() {
3359         
3360         if(!this.href || this.preventDefault){
3361             this.el.on('click', this.onClick, this);
3362         }
3363     },
3364     
3365     onClick : function(e)
3366     {
3367         if(this.preventDefault){
3368             e.preventDefault();
3369         }
3370         //Roo.log('img onclick');
3371         this.fireEvent('click', this, e);
3372     }
3373    
3374 });
3375
3376  /*
3377  * - LGPL
3378  *
3379  * header
3380  * 
3381  */
3382
3383 /**
3384  * @class Roo.bootstrap.Header
3385  * @extends Roo.bootstrap.Component
3386  * @children Roo.bootstrap.Component
3387  * Bootstrap Header class
3388  *
3389  * 
3390  * @cfg {String} html content of header
3391  * @cfg {Number} level (1|2|3|4|5|6) default 1
3392  * 
3393  * @constructor
3394  * Create a new Header
3395  * @param {Object} config The config object
3396  */
3397
3398
3399 Roo.bootstrap.Header  = function(config){
3400     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3401 };
3402
3403 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3404     
3405     //href : false,
3406     html : false,
3407     level : 1,
3408     
3409     
3410     
3411     getAutoCreate : function(){
3412         
3413         
3414         
3415         var cfg = {
3416             tag: 'h' + (1 *this.level),
3417             html: this.html || ''
3418         } ;
3419         
3420         return cfg;
3421     }
3422    
3423 });
3424
3425  
3426
3427  /**
3428  * @class Roo.bootstrap.MenuMgr
3429  * @licence LGPL
3430  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3431  * @singleton
3432  */
3433 Roo.bootstrap.menu.Manager = function(){
3434    var menus, active, groups = {}, attached = false, lastShow = new Date();
3435
3436    // private - called when first menu is created
3437    function init(){
3438        menus = {};
3439        active = new Roo.util.MixedCollection();
3440        Roo.get(document).addKeyListener(27, function(){
3441            if(active.length > 0){
3442                hideAll();
3443            }
3444        });
3445    }
3446
3447    // private
3448    function hideAll(){
3449        if(active && active.length > 0){
3450            var c = active.clone();
3451            c.each(function(m){
3452                m.hide();
3453            });
3454        }
3455    }
3456
3457    // private
3458    function onHide(m){
3459        active.remove(m);
3460        if(active.length < 1){
3461            Roo.get(document).un("mouseup", onMouseDown);
3462             
3463            attached = false;
3464        }
3465    }
3466
3467    // private
3468    function onShow(m){
3469        var last = active.last();
3470        lastShow = new Date();
3471        active.add(m);
3472        if(!attached){
3473           Roo.get(document).on("mouseup", onMouseDown);
3474            
3475            attached = true;
3476        }
3477        if(m.parentMenu){
3478           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3479           m.parentMenu.activeChild = m;
3480        }else if(last && last.isVisible()){
3481           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3482        }
3483    }
3484
3485    // private
3486    function onBeforeHide(m){
3487        if(m.activeChild){
3488            m.activeChild.hide();
3489        }
3490        if(m.autoHideTimer){
3491            clearTimeout(m.autoHideTimer);
3492            delete m.autoHideTimer;
3493        }
3494    }
3495
3496    // private
3497    function onBeforeShow(m){
3498        var pm = m.parentMenu;
3499        if(!pm && !m.allowOtherMenus){
3500            hideAll();
3501        }else if(pm && pm.activeChild && active != m){
3502            pm.activeChild.hide();
3503        }
3504    }
3505
3506    // private this should really trigger on mouseup..
3507    function onMouseDown(e){
3508         Roo.log("on Mouse Up");
3509         
3510         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3511             Roo.log("MenuManager hideAll");
3512             hideAll();
3513             e.stopEvent();
3514         }
3515         
3516         
3517    }
3518
3519    // private
3520    function onBeforeCheck(mi, state){
3521        if(state){
3522            var g = groups[mi.group];
3523            for(var i = 0, l = g.length; i < l; i++){
3524                if(g[i] != mi){
3525                    g[i].setChecked(false);
3526                }
3527            }
3528        }
3529    }
3530
3531    return {
3532
3533        /**
3534         * Hides all menus that are currently visible
3535         */
3536        hideAll : function(){
3537             hideAll();  
3538        },
3539
3540        // private
3541        register : function(menu){
3542            if(!menus){
3543                init();
3544            }
3545            menus[menu.id] = menu;
3546            menu.on("beforehide", onBeforeHide);
3547            menu.on("hide", onHide);
3548            menu.on("beforeshow", onBeforeShow);
3549            menu.on("show", onShow);
3550            var g = menu.group;
3551            if(g && menu.events["checkchange"]){
3552                if(!groups[g]){
3553                    groups[g] = [];
3554                }
3555                groups[g].push(menu);
3556                menu.on("checkchange", onCheck);
3557            }
3558        },
3559
3560         /**
3561          * Returns a {@link Roo.menu.Menu} object
3562          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3563          * be used to generate and return a new Menu instance.
3564          */
3565        get : function(menu){
3566            if(typeof menu == "string"){ // menu id
3567                return menus[menu];
3568            }else if(menu.events){  // menu instance
3569                return menu;
3570            }
3571            /*else if(typeof menu.length == 'number'){ // array of menu items?
3572                return new Roo.bootstrap.Menu({items:menu});
3573            }else{ // otherwise, must be a config
3574                return new Roo.bootstrap.Menu(menu);
3575            }
3576            */
3577            return false;
3578        },
3579
3580        // private
3581        unregister : function(menu){
3582            delete menus[menu.id];
3583            menu.un("beforehide", onBeforeHide);
3584            menu.un("hide", onHide);
3585            menu.un("beforeshow", onBeforeShow);
3586            menu.un("show", onShow);
3587            var g = menu.group;
3588            if(g && menu.events["checkchange"]){
3589                groups[g].remove(menu);
3590                menu.un("checkchange", onCheck);
3591            }
3592        },
3593
3594        // private
3595        registerCheckable : function(menuItem){
3596            var g = menuItem.group;
3597            if(g){
3598                if(!groups[g]){
3599                    groups[g] = [];
3600                }
3601                groups[g].push(menuItem);
3602                menuItem.on("beforecheckchange", onBeforeCheck);
3603            }
3604        },
3605
3606        // private
3607        unregisterCheckable : function(menuItem){
3608            var g = menuItem.group;
3609            if(g){
3610                groups[g].remove(menuItem);
3611                menuItem.un("beforecheckchange", onBeforeCheck);
3612            }
3613        }
3614    };
3615 }(); 
3616 /**
3617  * @class Roo.bootstrap.menu.Menu
3618  * @extends Roo.bootstrap.Component
3619  * @licence LGPL
3620  * @children Roo.bootstrap.menu.Item
3621  * Bootstrap Menu class - container for MenuItems
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  * @licence LGPL
4096  * Bootstrap MenuItem class
4097  * 
4098  * @cfg {String} html the menu label
4099  * @cfg {String} href the link
4100  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4101  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4102  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4103  * @cfg {String} fa favicon to show on left of menu item.
4104  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4105  * 
4106  * 
4107  * @constructor
4108  * Create a new MenuItem
4109  * @param {Object} config The config object
4110  */
4111
4112
4113 Roo.bootstrap.menu.Item = function(config){
4114     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4115     this.addEvents({
4116         // raw events
4117         /**
4118          * @event click
4119          * The raw click event for the entire grid.
4120          * @param {Roo.bootstrap.menu.Item} this
4121          * @param {Roo.EventObject} e
4122          */
4123         "click" : true
4124     });
4125 };
4126
4127 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4128     
4129     href : false,
4130     html : false,
4131     preventDefault: false,
4132     isContainer : false,
4133     active : false,
4134     fa: false,
4135     
4136     getAutoCreate : function(){
4137         
4138         if(this.isContainer){
4139             return {
4140                 tag: 'li',
4141                 cls: 'dropdown-menu-item '
4142             };
4143         }
4144         var ctag = {
4145             tag: 'span',
4146             html: 'Link'
4147         };
4148         
4149         var anc = {
4150             tag : 'a',
4151             cls : 'dropdown-item',
4152             href : '#',
4153             cn : [  ]
4154         };
4155         
4156         if (this.fa !== false) {
4157             anc.cn.push({
4158                 tag : 'i',
4159                 cls : 'fa fa-' + this.fa
4160             });
4161         }
4162         
4163         anc.cn.push(ctag);
4164         
4165         
4166         var cfg= {
4167             tag: 'li',
4168             cls: 'dropdown-menu-item',
4169             cn: [ anc ]
4170         };
4171         if (this.parent().type == 'treeview') {
4172             cfg.cls = 'treeview-menu';
4173         }
4174         if (this.active) {
4175             cfg.cls += ' active';
4176         }
4177         
4178         
4179         
4180         anc.href = this.href || cfg.cn[0].href ;
4181         ctag.html = this.html || cfg.cn[0].html ;
4182         return cfg;
4183     },
4184     
4185     initEvents: function()
4186     {
4187         if (this.parent().type == 'treeview') {
4188             this.el.select('a').on('click', this.onClick, this);
4189         }
4190         
4191         if (this.menu) {
4192             this.menu.parentType = this.xtype;
4193             this.menu.triggerEl = this.el;
4194             this.menu = this.addxtype(Roo.apply({}, this.menu));
4195         }
4196         
4197     },
4198     onClick : function(e)
4199     {
4200         Roo.log('item on click ');
4201         
4202         if(this.preventDefault){
4203             e.preventDefault();
4204         }
4205         //this.parent().hideMenuItems();
4206         
4207         this.fireEvent('click', this, e);
4208     },
4209     getEl : function()
4210     {
4211         return this.el;
4212     } 
4213 });
4214
4215  
4216
4217  
4218
4219   
4220 /**
4221  * @class Roo.bootstrap.menu.Separator
4222  * @extends Roo.bootstrap.Component
4223  * @licence LGPL
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @builder-top
4260  * @parent none
4261  * @children Roo.bootstrap.Component
4262  * Bootstrap Modal class
4263  * @cfg {String} title Title of dialog
4264  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4265  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4266  * @cfg {Boolean} specificTitle default false
4267  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4268  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4269  * @cfg {Boolean} animate default true
4270  * @cfg {Boolean} allow_close default true
4271  * @cfg {Boolean} fitwindow default false
4272  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4273  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4274  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4275  * @cfg {String} size (sm|lg|xl) default empty
4276  * @cfg {Number} max_width set the max width of modal
4277  * @cfg {Boolean} editableTitle can the title be edited
4278
4279  *
4280  *
4281  * @constructor
4282  * Create a new Modal Dialog
4283  * @param {Object} config The config object
4284  */
4285
4286 Roo.bootstrap.Modal = function(config){
4287     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4288     this.addEvents({
4289         // raw events
4290         /**
4291          * @event btnclick
4292          * The raw btnclick event for the button
4293          * @param {Roo.EventObject} e
4294          */
4295         "btnclick" : true,
4296         /**
4297          * @event resize
4298          * Fire when dialog resize
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} e
4301          */
4302         "resize" : true,
4303         /**
4304          * @event titlechanged
4305          * Fire when the editable title has been changed
4306          * @param {Roo.bootstrap.Modal} this
4307          * @param {Roo.EventObject} value
4308          */
4309         "titlechanged" : true 
4310         
4311     });
4312     this.buttons = this.buttons || [];
4313
4314     if (this.tmpl) {
4315         this.tmpl = Roo.factory(this.tmpl);
4316     }
4317
4318 };
4319
4320 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4321
4322     title : 'test dialog',
4323
4324     buttons : false,
4325
4326     // set on load...
4327
4328     html: false,
4329
4330     tmp: false,
4331
4332     specificTitle: false,
4333
4334     buttonPosition: 'right',
4335
4336     allow_close : true,
4337
4338     animate : true,
4339
4340     fitwindow: false,
4341     
4342      // private
4343     dialogEl: false,
4344     bodyEl:  false,
4345     footerEl:  false,
4346     titleEl:  false,
4347     closeEl:  false,
4348
4349     size: '',
4350     
4351     max_width: 0,
4352     
4353     max_height: 0,
4354     
4355     fit_content: false,
4356     editableTitle  : false,
4357
4358     onRender : function(ct, position)
4359     {
4360         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4361
4362         if(!this.el){
4363             var cfg = Roo.apply({},  this.getAutoCreate());
4364             cfg.id = Roo.id();
4365             //if(!cfg.name){
4366             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4367             //}
4368             //if (!cfg.name.length) {
4369             //    delete cfg.name;
4370            // }
4371             if (this.cls) {
4372                 cfg.cls += ' ' + this.cls;
4373             }
4374             if (this.style) {
4375                 cfg.style = this.style;
4376             }
4377             this.el = Roo.get(document.body).createChild(cfg, position);
4378         }
4379         //var type = this.el.dom.type;
4380
4381
4382         if(this.tabIndex !== undefined){
4383             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4384         }
4385
4386         this.dialogEl = this.el.select('.modal-dialog',true).first();
4387         this.bodyEl = this.el.select('.modal-body',true).first();
4388         this.closeEl = this.el.select('.modal-header .close', true).first();
4389         this.headerEl = this.el.select('.modal-header',true).first();
4390         this.titleEl = this.el.select('.modal-title',true).first();
4391         this.footerEl = this.el.select('.modal-footer',true).first();
4392
4393         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4394         
4395         //this.el.addClass("x-dlg-modal");
4396
4397         if (this.buttons.length) {
4398             Roo.each(this.buttons, function(bb) {
4399                 var b = Roo.apply({}, bb);
4400                 b.xns = b.xns || Roo.bootstrap;
4401                 b.xtype = b.xtype || 'Button';
4402                 if (typeof(b.listeners) == 'undefined') {
4403                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4404                 }
4405
4406                 var btn = Roo.factory(b);
4407
4408                 btn.render(this.getButtonContainer());
4409
4410             },this);
4411         }
4412         // render the children.
4413         var nitems = [];
4414
4415         if(typeof(this.items) != 'undefined'){
4416             var items = this.items;
4417             delete this.items;
4418
4419             for(var i =0;i < items.length;i++) {
4420                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4421             }
4422         }
4423
4424         this.items = nitems;
4425
4426         // where are these used - they used to be body/close/footer
4427
4428
4429         this.initEvents();
4430         //this.el.addClass([this.fieldClass, this.cls]);
4431
4432     },
4433
4434     getAutoCreate : function()
4435     {
4436         // we will default to modal-body-overflow - might need to remove or make optional later.
4437         var bdy = {
4438                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4439                 html : this.html || ''
4440         };
4441
4442         var title = {
4443             tag: 'h5',
4444             cls : 'modal-title',
4445             html : this.title
4446         };
4447
4448         if(this.specificTitle){ // WTF is this?
4449             title = this.title;
4450         }
4451
4452         var header = [];
4453         if (this.allow_close && Roo.bootstrap.version == 3) {
4454             header.push({
4455                 tag: 'button',
4456                 cls : 'close',
4457                 html : '&times'
4458             });
4459         }
4460
4461         header.push(title);
4462
4463         if (this.editableTitle) {
4464             header.push({
4465                 cls: 'form-control roo-editable-title d-none',
4466                 tag: 'input',
4467                 type: 'text'
4468             });
4469         }
4470         
4471         if (this.allow_close && Roo.bootstrap.version == 4) {
4472             header.push({
4473                 tag: 'button',
4474                 cls : 'close',
4475                 html : '&times'
4476             });
4477         }
4478         
4479         var size = '';
4480
4481         if(this.size.length){
4482             size = 'modal-' + this.size;
4483         }
4484         
4485         var footer = Roo.bootstrap.version == 3 ?
4486             {
4487                 cls : 'modal-footer',
4488                 cn : [
4489                     {
4490                         tag: 'div',
4491                         cls: 'btn-' + this.buttonPosition
4492                     }
4493                 ]
4494
4495             } :
4496             {  // BS4 uses mr-auto on left buttons....
4497                 cls : 'modal-footer'
4498             };
4499
4500             
4501
4502         
4503         
4504         var modal = {
4505             cls: "modal",
4506              cn : [
4507                 {
4508                     cls: "modal-dialog " + size,
4509                     cn : [
4510                         {
4511                             cls : "modal-content",
4512                             cn : [
4513                                 {
4514                                     cls : 'modal-header',
4515                                     cn : header
4516                                 },
4517                                 bdy,
4518                                 footer
4519                             ]
4520
4521                         }
4522                     ]
4523
4524                 }
4525             ]
4526         };
4527
4528         if(this.animate){
4529             modal.cls += ' fade';
4530         }
4531
4532         return modal;
4533
4534     },
4535     getChildContainer : function() {
4536
4537          return this.bodyEl;
4538
4539     },
4540     getButtonContainer : function() {
4541         
4542          return Roo.bootstrap.version == 4 ?
4543             this.el.select('.modal-footer',true).first()
4544             : this.el.select('.modal-footer div',true).first();
4545
4546     },
4547     initEvents : function()
4548     {
4549         if (this.allow_close) {
4550             this.closeEl.on('click', this.hide, this);
4551         }
4552         Roo.EventManager.onWindowResize(this.resize, this, true);
4553         if (this.editableTitle) {
4554             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4555             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4556             this.headerEditEl.on('keyup', function(e) {
4557                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4558                         this.toggleHeaderInput(false)
4559                     }
4560                 }, this);
4561             this.headerEditEl.on('blur', function(e) {
4562                 this.toggleHeaderInput(false)
4563             },this);
4564         }
4565
4566     },
4567   
4568
4569     resize : function()
4570     {
4571         this.maskEl.setSize(
4572             Roo.lib.Dom.getViewWidth(true),
4573             Roo.lib.Dom.getViewHeight(true)
4574         );
4575         
4576         if (this.fitwindow) {
4577             
4578            this.dialogEl.setStyle( { 'max-width' : '100%' });
4579             this.setSize(
4580                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4581                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4582             );
4583             return;
4584         }
4585         
4586         if(this.max_width !== 0) {
4587             
4588             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4589             
4590             if(this.height) {
4591                 this.setSize(w, this.height);
4592                 return;
4593             }
4594             
4595             if(this.max_height) {
4596                 this.setSize(w,Math.min(
4597                     this.max_height,
4598                     Roo.lib.Dom.getViewportHeight(true) - 60
4599                 ));
4600                 
4601                 return;
4602             }
4603             
4604             if(!this.fit_content) {
4605                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4606                 return;
4607             }
4608             
4609             this.setSize(w, Math.min(
4610                 60 +
4611                 this.headerEl.getHeight() + 
4612                 this.footerEl.getHeight() + 
4613                 this.getChildHeight(this.bodyEl.dom.childNodes),
4614                 Roo.lib.Dom.getViewportHeight(true) - 60)
4615             );
4616         }
4617         
4618     },
4619
4620     setSize : function(w,h)
4621     {
4622         if (!w && !h) {
4623             return;
4624         }
4625         
4626         this.resizeTo(w,h);
4627     },
4628
4629     show : function() {
4630
4631         if (!this.rendered) {
4632             this.render();
4633         }
4634         this.toggleHeaderInput(false);
4635         //this.el.setStyle('display', 'block');
4636         this.el.removeClass('hideing');
4637         this.el.dom.style.display='block';
4638         
4639         Roo.get(document.body).addClass('modal-open');
4640  
4641         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4642             
4643             (function(){
4644                 this.el.addClass('show');
4645                 this.el.addClass('in');
4646             }).defer(50, this);
4647         }else{
4648             this.el.addClass('show');
4649             this.el.addClass('in');
4650         }
4651
4652         // not sure how we can show data in here..
4653         //if (this.tmpl) {
4654         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4655         //}
4656
4657         Roo.get(document.body).addClass("x-body-masked");
4658         
4659         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4660         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4661         this.maskEl.dom.style.display = 'block';
4662         this.maskEl.addClass('show');
4663         
4664         
4665         this.resize();
4666         
4667         this.fireEvent('show', this);
4668
4669         // set zindex here - otherwise it appears to be ignored...
4670         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4671
4672         (function () {
4673             this.items.forEach( function(e) {
4674                 e.layout ? e.layout() : false;
4675
4676             });
4677         }).defer(100,this);
4678
4679     },
4680     hide : function()
4681     {
4682         if(this.fireEvent("beforehide", this) !== false){
4683             
4684             this.maskEl.removeClass('show');
4685             
4686             this.maskEl.dom.style.display = '';
4687             Roo.get(document.body).removeClass("x-body-masked");
4688             this.el.removeClass('in');
4689             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4690
4691             if(this.animate){ // why
4692                 this.el.addClass('hideing');
4693                 this.el.removeClass('show');
4694                 (function(){
4695                     if (!this.el.hasClass('hideing')) {
4696                         return; // it's been shown again...
4697                     }
4698                     
4699                     this.el.dom.style.display='';
4700
4701                     Roo.get(document.body).removeClass('modal-open');
4702                     this.el.removeClass('hideing');
4703                 }).defer(150,this);
4704                 
4705             }else{
4706                 this.el.removeClass('show');
4707                 this.el.dom.style.display='';
4708                 Roo.get(document.body).removeClass('modal-open');
4709
4710             }
4711             this.fireEvent('hide', this);
4712         }
4713     },
4714     isVisible : function()
4715     {
4716         
4717         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4718         
4719     },
4720
4721     addButton : function(str, cb)
4722     {
4723
4724
4725         var b = Roo.apply({}, { html : str } );
4726         b.xns = b.xns || Roo.bootstrap;
4727         b.xtype = b.xtype || 'Button';
4728         if (typeof(b.listeners) == 'undefined') {
4729             b.listeners = { click : cb.createDelegate(this)  };
4730         }
4731
4732         var btn = Roo.factory(b);
4733
4734         btn.render(this.getButtonContainer());
4735
4736         return btn;
4737
4738     },
4739
4740     setDefaultButton : function(btn)
4741     {
4742         //this.el.select('.modal-footer').()
4743     },
4744
4745     resizeTo: function(w,h)
4746     {
4747         this.dialogEl.setWidth(w);
4748         
4749         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4750
4751         this.bodyEl.setHeight(h - diff);
4752         
4753         this.fireEvent('resize', this);
4754     },
4755     
4756     setContentSize  : function(w, h)
4757     {
4758
4759     },
4760     onButtonClick: function(btn,e)
4761     {
4762         //Roo.log([a,b,c]);
4763         this.fireEvent('btnclick', btn.name, e);
4764     },
4765      /**
4766      * Set the title of the Dialog
4767      * @param {String} str new Title
4768      */
4769     setTitle: function(str) {
4770         this.titleEl.dom.innerHTML = str;
4771         this.title = str;
4772     },
4773     /**
4774      * Set the body of the Dialog
4775      * @param {String} str new Title
4776      */
4777     setBody: function(str) {
4778         this.bodyEl.dom.innerHTML = str;
4779     },
4780     /**
4781      * Set the body of the Dialog using the template
4782      * @param {Obj} data - apply this data to the template and replace the body contents.
4783      */
4784     applyBody: function(obj)
4785     {
4786         if (!this.tmpl) {
4787             Roo.log("Error - using apply Body without a template");
4788             //code
4789         }
4790         this.tmpl.overwrite(this.bodyEl, obj);
4791     },
4792     
4793     getChildHeight : function(child_nodes)
4794     {
4795         if(
4796             !child_nodes ||
4797             child_nodes.length == 0
4798         ) {
4799             return 0;
4800         }
4801         
4802         var child_height = 0;
4803         
4804         for(var i = 0; i < child_nodes.length; i++) {
4805             
4806             /*
4807             * for modal with tabs...
4808             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4809                 
4810                 var layout_childs = child_nodes[i].childNodes;
4811                 
4812                 for(var j = 0; j < layout_childs.length; j++) {
4813                     
4814                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4815                         
4816                         var layout_body_childs = layout_childs[j].childNodes;
4817                         
4818                         for(var k = 0; k < layout_body_childs.length; k++) {
4819                             
4820                             if(layout_body_childs[k].classList.contains('navbar')) {
4821                                 child_height += layout_body_childs[k].offsetHeight;
4822                                 continue;
4823                             }
4824                             
4825                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4826                                 
4827                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4828                                 
4829                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4830                                     
4831                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4832                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4833                                         continue;
4834                                     }
4835                                     
4836                                 }
4837                                 
4838                             }
4839                             
4840                         }
4841                     }
4842                 }
4843                 continue;
4844             }
4845             */
4846             
4847             child_height += child_nodes[i].offsetHeight;
4848             // Roo.log(child_nodes[i].offsetHeight);
4849         }
4850         
4851         return child_height;
4852     },
4853     toggleHeaderInput : function(is_edit)
4854     {
4855         if (!this.editableTitle) {
4856             return; // not editable.
4857         }
4858         if (is_edit && this.is_header_editing) {
4859             return; // already editing..
4860         }
4861         if (is_edit) {
4862     
4863             this.headerEditEl.dom.value = this.title;
4864             this.headerEditEl.removeClass('d-none');
4865             this.headerEditEl.dom.focus();
4866             this.titleEl.addClass('d-none');
4867             
4868             this.is_header_editing = true;
4869             return
4870         }
4871         // flip back to not editing.
4872         this.title = this.headerEditEl.dom.value;
4873         this.headerEditEl.addClass('d-none');
4874         this.titleEl.removeClass('d-none');
4875         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4876         this.is_header_editing = false;
4877         this.fireEvent('titlechanged', this, this.title);
4878     
4879             
4880         
4881     }
4882
4883 });
4884
4885
4886 Roo.apply(Roo.bootstrap.Modal,  {
4887     /**
4888          * Button config that displays a single OK button
4889          * @type Object
4890          */
4891         OK :  [{
4892             name : 'ok',
4893             weight : 'primary',
4894             html : 'OK'
4895         }],
4896         /**
4897          * Button config that displays Yes and No buttons
4898          * @type Object
4899          */
4900         YESNO : [
4901             {
4902                 name  : 'no',
4903                 html : 'No'
4904             },
4905             {
4906                 name  :'yes',
4907                 weight : 'primary',
4908                 html : 'Yes'
4909             }
4910         ],
4911
4912         /**
4913          * Button config that displays OK and Cancel buttons
4914          * @type Object
4915          */
4916         OKCANCEL : [
4917             {
4918                name : 'cancel',
4919                 html : 'Cancel'
4920             },
4921             {
4922                 name : 'ok',
4923                 weight : 'primary',
4924                 html : 'OK'
4925             }
4926         ],
4927         /**
4928          * Button config that displays Yes, No and Cancel buttons
4929          * @type Object
4930          */
4931         YESNOCANCEL : [
4932             {
4933                 name : 'yes',
4934                 weight : 'primary',
4935                 html : 'Yes'
4936             },
4937             {
4938                 name : 'no',
4939                 html : 'No'
4940             },
4941             {
4942                 name : 'cancel',
4943                 html : 'Cancel'
4944             }
4945         ],
4946         
4947         zIndex : 10001
4948 });
4949
4950 /*
4951  * - LGPL
4952  *
4953  * messagebox - can be used as a replace
4954  * 
4955  */
4956 /**
4957  * @class Roo.MessageBox
4958  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4959  * Example usage:
4960  *<pre><code>
4961 // Basic alert:
4962 Roo.Msg.alert('Status', 'Changes saved successfully.');
4963
4964 // Prompt for user data:
4965 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4966     if (btn == 'ok'){
4967         // process text value...
4968     }
4969 });
4970
4971 // Show a dialog using config options:
4972 Roo.Msg.show({
4973    title:'Save Changes?',
4974    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4975    buttons: Roo.Msg.YESNOCANCEL,
4976    fn: processResult,
4977    animEl: 'elId'
4978 });
4979 </code></pre>
4980  * @singleton
4981  */
4982 Roo.bootstrap.MessageBox = function(){
4983     var dlg, opt, mask, waitTimer;
4984     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4985     var buttons, activeTextEl, bwidth;
4986
4987     
4988     // private
4989     var handleButton = function(button){
4990         dlg.hide();
4991         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4992     };
4993
4994     // private
4995     var handleHide = function(){
4996         if(opt && opt.cls){
4997             dlg.el.removeClass(opt.cls);
4998         }
4999         //if(waitTimer){
5000         //    Roo.TaskMgr.stop(waitTimer);
5001         //    waitTimer = null;
5002         //}
5003     };
5004
5005     // private
5006     var updateButtons = function(b){
5007         var width = 0;
5008         if(!b){
5009             buttons["ok"].hide();
5010             buttons["cancel"].hide();
5011             buttons["yes"].hide();
5012             buttons["no"].hide();
5013             dlg.footerEl.hide();
5014             
5015             return width;
5016         }
5017         dlg.footerEl.show();
5018         for(var k in buttons){
5019             if(typeof buttons[k] != "function"){
5020                 if(b[k]){
5021                     buttons[k].show();
5022                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5023                     width += buttons[k].el.getWidth()+15;
5024                 }else{
5025                     buttons[k].hide();
5026                 }
5027             }
5028         }
5029         return width;
5030     };
5031
5032     // private
5033     var handleEsc = function(d, k, e){
5034         if(opt && opt.closable !== false){
5035             dlg.hide();
5036         }
5037         if(e){
5038             e.stopEvent();
5039         }
5040     };
5041
5042     return {
5043         /**
5044          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5045          * @return {Roo.BasicDialog} The BasicDialog element
5046          */
5047         getDialog : function(){
5048            if(!dlg){
5049                 dlg = new Roo.bootstrap.Modal( {
5050                     //draggable: true,
5051                     //resizable:false,
5052                     //constraintoviewport:false,
5053                     //fixedcenter:true,
5054                     //collapsible : false,
5055                     //shim:true,
5056                     //modal: true,
5057                 //    width: 'auto',
5058                   //  height:100,
5059                     //buttonAlign:"center",
5060                     closeClick : function(){
5061                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5062                             handleButton("no");
5063                         }else{
5064                             handleButton("cancel");
5065                         }
5066                     }
5067                 });
5068                 dlg.render();
5069                 dlg.on("hide", handleHide);
5070                 mask = dlg.mask;
5071                 //dlg.addKeyListener(27, handleEsc);
5072                 buttons = {};
5073                 this.buttons = buttons;
5074                 var bt = this.buttonText;
5075                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5076                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5077                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5078                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5079                 //Roo.log(buttons);
5080                 bodyEl = dlg.bodyEl.createChild({
5081
5082                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5083                         '<textarea class="roo-mb-textarea"></textarea>' +
5084                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5085                 });
5086                 msgEl = bodyEl.dom.firstChild;
5087                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5088                 textboxEl.enableDisplayMode();
5089                 textboxEl.addKeyListener([10,13], function(){
5090                     if(dlg.isVisible() && opt && opt.buttons){
5091                         if(opt.buttons.ok){
5092                             handleButton("ok");
5093                         }else if(opt.buttons.yes){
5094                             handleButton("yes");
5095                         }
5096                     }
5097                 });
5098                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5099                 textareaEl.enableDisplayMode();
5100                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5101                 progressEl.enableDisplayMode();
5102                 
5103                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5104                 var pf = progressEl.dom.firstChild;
5105                 if (pf) {
5106                     pp = Roo.get(pf.firstChild);
5107                     pp.setHeight(pf.offsetHeight);
5108                 }
5109                 
5110             }
5111             return dlg;
5112         },
5113
5114         /**
5115          * Updates the message box body text
5116          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5117          * the XHTML-compliant non-breaking space character '&amp;#160;')
5118          * @return {Roo.MessageBox} This message box
5119          */
5120         updateText : function(text)
5121         {
5122             if(!dlg.isVisible() && !opt.width){
5123                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5124                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5125             }
5126             msgEl.innerHTML = text || '&#160;';
5127       
5128             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5129             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5130             var w = Math.max(
5131                     Math.min(opt.width || cw , this.maxWidth), 
5132                     Math.max(opt.minWidth || this.minWidth, bwidth)
5133             );
5134             if(opt.prompt){
5135                 activeTextEl.setWidth(w);
5136             }
5137             if(dlg.isVisible()){
5138                 dlg.fixedcenter = false;
5139             }
5140             // to big, make it scroll. = But as usual stupid IE does not support
5141             // !important..
5142             
5143             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5144                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5145                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5146             } else {
5147                 bodyEl.dom.style.height = '';
5148                 bodyEl.dom.style.overflowY = '';
5149             }
5150             if (cw > w) {
5151                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5152             } else {
5153                 bodyEl.dom.style.overflowX = '';
5154             }
5155             
5156             dlg.setContentSize(w, bodyEl.getHeight());
5157             if(dlg.isVisible()){
5158                 dlg.fixedcenter = true;
5159             }
5160             return this;
5161         },
5162
5163         /**
5164          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5165          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5166          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5167          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5168          * @return {Roo.MessageBox} This message box
5169          */
5170         updateProgress : function(value, text){
5171             if(text){
5172                 this.updateText(text);
5173             }
5174             
5175             if (pp) { // weird bug on my firefox - for some reason this is not defined
5176                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5177                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5178             }
5179             return this;
5180         },        
5181
5182         /**
5183          * Returns true if the message box is currently displayed
5184          * @return {Boolean} True if the message box is visible, else false
5185          */
5186         isVisible : function(){
5187             return dlg && dlg.isVisible();  
5188         },
5189
5190         /**
5191          * Hides the message box if it is displayed
5192          */
5193         hide : function(){
5194             if(this.isVisible()){
5195                 dlg.hide();
5196             }  
5197         },
5198
5199         /**
5200          * Displays a new message box, or reinitializes an existing message box, based on the config options
5201          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5202          * The following config object properties are supported:
5203          * <pre>
5204 Property    Type             Description
5205 ----------  ---------------  ------------------------------------------------------------------------------------
5206 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5207                                    closes (defaults to undefined)
5208 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5209                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5210 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5211                                    progress and wait dialogs will ignore this property and always hide the
5212                                    close button as they can only be closed programmatically.
5213 cls               String           A custom CSS class to apply to the message box element
5214 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5215                                    displayed (defaults to 75)
5216 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5217                                    function will be btn (the name of the button that was clicked, if applicable,
5218                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5219                                    Progress and wait dialogs will ignore this option since they do not respond to
5220                                    user actions and can only be closed programmatically, so any required function
5221                                    should be called by the same code after it closes the dialog.
5222 icon              String           A CSS class that provides a background image to be used as an icon for
5223                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5224 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5225 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5226 modal             Boolean          False to allow user interaction with the page while the message box is
5227                                    displayed (defaults to true)
5228 msg               String           A string that will replace the existing message box body text (defaults
5229                                    to the XHTML-compliant non-breaking space character '&#160;')
5230 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5231 progress          Boolean          True to display a progress bar (defaults to false)
5232 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5233 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5234 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5235 title             String           The title text
5236 value             String           The string value to set into the active textbox element if displayed
5237 wait              Boolean          True to display a progress bar (defaults to false)
5238 width             Number           The width of the dialog in pixels
5239 </pre>
5240          *
5241          * Example usage:
5242          * <pre><code>
5243 Roo.Msg.show({
5244    title: 'Address',
5245    msg: 'Please enter your address:',
5246    width: 300,
5247    buttons: Roo.MessageBox.OKCANCEL,
5248    multiline: true,
5249    fn: saveAddress,
5250    animEl: 'addAddressBtn'
5251 });
5252 </code></pre>
5253          * @param {Object} config Configuration options
5254          * @return {Roo.MessageBox} This message box
5255          */
5256         show : function(options)
5257         {
5258             
5259             // this causes nightmares if you show one dialog after another
5260             // especially on callbacks..
5261              
5262             if(this.isVisible()){
5263                 
5264                 this.hide();
5265                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5266                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5267                 Roo.log("New Dialog Message:" +  options.msg )
5268                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5269                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5270                 
5271             }
5272             var d = this.getDialog();
5273             opt = options;
5274             d.setTitle(opt.title || "&#160;");
5275             d.closeEl.setDisplayed(opt.closable !== false);
5276             activeTextEl = textboxEl;
5277             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5278             if(opt.prompt){
5279                 if(opt.multiline){
5280                     textboxEl.hide();
5281                     textareaEl.show();
5282                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5283                         opt.multiline : this.defaultTextHeight);
5284                     activeTextEl = textareaEl;
5285                 }else{
5286                     textboxEl.show();
5287                     textareaEl.hide();
5288                 }
5289             }else{
5290                 textboxEl.hide();
5291                 textareaEl.hide();
5292             }
5293             progressEl.setDisplayed(opt.progress === true);
5294             if (opt.progress) {
5295                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5296             }
5297             this.updateProgress(0);
5298             activeTextEl.dom.value = opt.value || "";
5299             if(opt.prompt){
5300                 dlg.setDefaultButton(activeTextEl);
5301             }else{
5302                 var bs = opt.buttons;
5303                 var db = null;
5304                 if(bs && bs.ok){
5305                     db = buttons["ok"];
5306                 }else if(bs && bs.yes){
5307                     db = buttons["yes"];
5308                 }
5309                 dlg.setDefaultButton(db);
5310             }
5311             bwidth = updateButtons(opt.buttons);
5312             this.updateText(opt.msg);
5313             if(opt.cls){
5314                 d.el.addClass(opt.cls);
5315             }
5316             d.proxyDrag = opt.proxyDrag === true;
5317             d.modal = opt.modal !== false;
5318             d.mask = opt.modal !== false ? mask : false;
5319             if(!d.isVisible()){
5320                 // force it to the end of the z-index stack so it gets a cursor in FF
5321                 document.body.appendChild(dlg.el.dom);
5322                 d.animateTarget = null;
5323                 d.show(options.animEl);
5324             }
5325             return this;
5326         },
5327
5328         /**
5329          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5330          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5331          * and closing the message box when the process is complete.
5332          * @param {String} title The title bar text
5333          * @param {String} msg The message box body text
5334          * @return {Roo.MessageBox} This message box
5335          */
5336         progress : function(title, msg){
5337             this.show({
5338                 title : title,
5339                 msg : msg,
5340                 buttons: false,
5341                 progress:true,
5342                 closable:false,
5343                 minWidth: this.minProgressWidth,
5344                 modal : true
5345             });
5346             return this;
5347         },
5348
5349         /**
5350          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5351          * If a callback function is passed it will be called after the user clicks the button, and the
5352          * id of the button that was clicked will be passed as the only parameter to the callback
5353          * (could also be the top-right close button).
5354          * @param {String} title The title bar text
5355          * @param {String} msg The message box body text
5356          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5357          * @param {Object} scope (optional) The scope of the callback function
5358          * @return {Roo.MessageBox} This message box
5359          */
5360         alert : function(title, msg, fn, scope)
5361         {
5362             this.show({
5363                 title : title,
5364                 msg : msg,
5365                 buttons: this.OK,
5366                 fn: fn,
5367                 closable : false,
5368                 scope : scope,
5369                 modal : true
5370             });
5371             return this;
5372         },
5373
5374         /**
5375          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5376          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5377          * You are responsible for closing the message box when the process is complete.
5378          * @param {String} msg The message box body text
5379          * @param {String} title (optional) The title bar text
5380          * @return {Roo.MessageBox} This message box
5381          */
5382         wait : function(msg, title){
5383             this.show({
5384                 title : title,
5385                 msg : msg,
5386                 buttons: false,
5387                 closable:false,
5388                 progress:true,
5389                 modal:true,
5390                 width:300,
5391                 wait:true
5392             });
5393             waitTimer = Roo.TaskMgr.start({
5394                 run: function(i){
5395                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396                 },
5397                 interval: 1000
5398             });
5399             return this;
5400         },
5401
5402         /**
5403          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5404          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5405          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5406          * @param {String} title The title bar text
5407          * @param {String} msg The message box body text
5408          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5409          * @param {Object} scope (optional) The scope of the callback function
5410          * @return {Roo.MessageBox} This message box
5411          */
5412         confirm : function(title, msg, fn, scope){
5413             this.show({
5414                 title : title,
5415                 msg : msg,
5416                 buttons: this.YESNO,
5417                 fn: fn,
5418                 scope : scope,
5419                 modal : true
5420             });
5421             return this;
5422         },
5423
5424         /**
5425          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5426          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5427          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5428          * (could also be the top-right close button) and the text that was entered will be passed as the two
5429          * parameters to the callback.
5430          * @param {String} title The title bar text
5431          * @param {String} msg The message box body text
5432          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5433          * @param {Object} scope (optional) The scope of the callback function
5434          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5435          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5436          * @return {Roo.MessageBox} This message box
5437          */
5438         prompt : function(title, msg, fn, scope, multiline){
5439             this.show({
5440                 title : title,
5441                 msg : msg,
5442                 buttons: this.OKCANCEL,
5443                 fn: fn,
5444                 minWidth:250,
5445                 scope : scope,
5446                 prompt:true,
5447                 multiline: multiline,
5448                 modal : true
5449             });
5450             return this;
5451         },
5452
5453         /**
5454          * Button config that displays a single OK button
5455          * @type Object
5456          */
5457         OK : {ok:true},
5458         /**
5459          * Button config that displays Yes and No buttons
5460          * @type Object
5461          */
5462         YESNO : {yes:true, no:true},
5463         /**
5464          * Button config that displays OK and Cancel buttons
5465          * @type Object
5466          */
5467         OKCANCEL : {ok:true, cancel:true},
5468         /**
5469          * Button config that displays Yes, No and Cancel buttons
5470          * @type Object
5471          */
5472         YESNOCANCEL : {yes:true, no:true, cancel:true},
5473
5474         /**
5475          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5476          * @type Number
5477          */
5478         defaultTextHeight : 75,
5479         /**
5480          * The maximum width in pixels of the message box (defaults to 600)
5481          * @type Number
5482          */
5483         maxWidth : 600,
5484         /**
5485          * The minimum width in pixels of the message box (defaults to 100)
5486          * @type Number
5487          */
5488         minWidth : 100,
5489         /**
5490          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5491          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5492          * @type Number
5493          */
5494         minProgressWidth : 250,
5495         /**
5496          * An object containing the default button text strings that can be overriden for localized language support.
5497          * Supported properties are: ok, cancel, yes and no.
5498          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5499          * @type Object
5500          */
5501         buttonText : {
5502             ok : "OK",
5503             cancel : "Cancel",
5504             yes : "Yes",
5505             no : "No"
5506         }
5507     };
5508 }();
5509
5510 /**
5511  * Shorthand for {@link Roo.MessageBox}
5512  */
5513 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5514 Roo.Msg = Roo.Msg || Roo.MessageBox;
5515 /*
5516  * - LGPL
5517  *
5518  * navbar
5519  * 
5520  */
5521
5522 /**
5523  * @class Roo.bootstrap.nav.Bar
5524  * @extends Roo.bootstrap.Component
5525  * @abstract
5526  * Bootstrap Navbar class
5527
5528  * @constructor
5529  * Create a new Navbar
5530  * @param {Object} config The config object
5531  */
5532
5533
5534 Roo.bootstrap.nav.Bar = function(config){
5535     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5536     this.addEvents({
5537         // raw events
5538         /**
5539          * @event beforetoggle
5540          * Fire before toggle the menu
5541          * @param {Roo.EventObject} e
5542          */
5543         "beforetoggle" : true
5544     });
5545 };
5546
5547 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5548     
5549     
5550    
5551     // private
5552     navItems : false,
5553     loadMask : false,
5554     
5555     
5556     getAutoCreate : function(){
5557         
5558         
5559         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5560         
5561     },
5562     
5563     initEvents :function ()
5564     {
5565         //Roo.log(this.el.select('.navbar-toggle',true));
5566         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5567         
5568         var mark = {
5569             tag: "div",
5570             cls:"x-dlg-mask"
5571         };
5572         
5573         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5574         
5575         var size = this.el.getSize();
5576         this.maskEl.setSize(size.width, size.height);
5577         this.maskEl.enableDisplayMode("block");
5578         this.maskEl.hide();
5579         
5580         if(this.loadMask){
5581             this.maskEl.show();
5582         }
5583     },
5584     
5585     
5586     getChildContainer : function()
5587     {
5588         if (this.el && this.el.select('.collapse').getCount()) {
5589             return this.el.select('.collapse',true).first();
5590         }
5591         
5592         return this.el;
5593     },
5594     
5595     mask : function()
5596     {
5597         this.maskEl.show();
5598     },
5599     
5600     unmask : function()
5601     {
5602         this.maskEl.hide();
5603     },
5604     onToggle : function()
5605     {
5606         
5607         if(this.fireEvent('beforetoggle', this) === false){
5608             return;
5609         }
5610         var ce = this.el.select('.navbar-collapse',true).first();
5611       
5612         if (!ce.hasClass('show')) {
5613            this.expand();
5614         } else {
5615             this.collapse();
5616         }
5617         
5618         
5619     
5620     },
5621     /**
5622      * Expand the navbar pulldown 
5623      */
5624     expand : function ()
5625     {
5626        
5627         var ce = this.el.select('.navbar-collapse',true).first();
5628         if (ce.hasClass('collapsing')) {
5629             return;
5630         }
5631         ce.dom.style.height = '';
5632                // show it...
5633         ce.addClass('in'); // old...
5634         ce.removeClass('collapse');
5635         ce.addClass('show');
5636         var h = ce.getHeight();
5637         Roo.log(h);
5638         ce.removeClass('show');
5639         // at this point we should be able to see it..
5640         ce.addClass('collapsing');
5641         
5642         ce.setHeight(0); // resize it ...
5643         ce.on('transitionend', function() {
5644             //Roo.log('done transition');
5645             ce.removeClass('collapsing');
5646             ce.addClass('show');
5647             ce.removeClass('collapse');
5648
5649             ce.dom.style.height = '';
5650         }, this, { single: true} );
5651         ce.setHeight(h);
5652         ce.dom.scrollTop = 0;
5653     },
5654     /**
5655      * Collapse the navbar pulldown 
5656      */
5657     collapse : function()
5658     {
5659          var ce = this.el.select('.navbar-collapse',true).first();
5660        
5661         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5662             // it's collapsed or collapsing..
5663             return;
5664         }
5665         ce.removeClass('in'); // old...
5666         ce.setHeight(ce.getHeight());
5667         ce.removeClass('show');
5668         ce.addClass('collapsing');
5669         
5670         ce.on('transitionend', function() {
5671             ce.dom.style.height = '';
5672             ce.removeClass('collapsing');
5673             ce.addClass('collapse');
5674         }, this, { single: true} );
5675         ce.setHeight(0);
5676     }
5677     
5678     
5679     
5680 });
5681
5682
5683
5684  
5685
5686  /*
5687  * - LGPL
5688  *
5689  * navbar
5690  * 
5691  */
5692
5693 /**
5694  * @class Roo.bootstrap.nav.Simplebar
5695  * @extends Roo.bootstrap.nav.Bar
5696  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5697  * Bootstrap Sidebar class
5698  *
5699  * @cfg {Boolean} inverse is inverted color
5700  * 
5701  * @cfg {String} type (nav | pills | tabs)
5702  * @cfg {Boolean} arrangement stacked | justified
5703  * @cfg {String} align (left | right) alignment
5704  * 
5705  * @cfg {Boolean} main (true|false) main nav bar? default false
5706  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5707  * 
5708  * @cfg {String} tag (header|footer|nav|div) default is nav 
5709
5710  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5711  * 
5712  * 
5713  * @constructor
5714  * Create a new Sidebar
5715  * @param {Object} config The config object
5716  */
5717
5718
5719 Roo.bootstrap.nav.Simplebar = function(config){
5720     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5721 };
5722
5723 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5724     
5725     inverse: false,
5726     
5727     type: false,
5728     arrangement: '',
5729     align : false,
5730     
5731     weight : 'light',
5732     
5733     main : false,
5734     
5735     
5736     tag : false,
5737     
5738     
5739     getAutoCreate : function(){
5740         
5741         
5742         var cfg = {
5743             tag : this.tag || 'div',
5744             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5745         };
5746         if (['light','white'].indexOf(this.weight) > -1) {
5747             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5748         }
5749         cfg.cls += ' bg-' + this.weight;
5750         
5751         if (this.inverse) {
5752             cfg.cls += ' navbar-inverse';
5753             
5754         }
5755         
5756         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5757         
5758         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5759             return cfg;
5760         }
5761         
5762         
5763     
5764         
5765         cfg.cn = [
5766             {
5767                 cls: 'nav nav-' + this.xtype,
5768                 tag : 'ul'
5769             }
5770         ];
5771         
5772          
5773         this.type = this.type || 'nav';
5774         if (['tabs','pills'].indexOf(this.type) != -1) {
5775             cfg.cn[0].cls += ' nav-' + this.type
5776         
5777         
5778         } else {
5779             if (this.type!=='nav') {
5780                 Roo.log('nav type must be nav/tabs/pills')
5781             }
5782             cfg.cn[0].cls += ' navbar-nav'
5783         }
5784         
5785         
5786         
5787         
5788         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5789             cfg.cn[0].cls += ' nav-' + this.arrangement;
5790         }
5791         
5792         
5793         if (this.align === 'right') {
5794             cfg.cn[0].cls += ' navbar-right';
5795         }
5796         
5797         
5798         
5799         
5800         return cfg;
5801     
5802         
5803     }
5804     
5805     
5806     
5807 });
5808
5809
5810
5811  
5812
5813  
5814        /*
5815  * - LGPL
5816  *
5817  * navbar
5818  * navbar-fixed-top
5819  * navbar-expand-md  fixed-top 
5820  */
5821
5822 /**
5823  * @class Roo.bootstrap.nav.Headerbar
5824  * @extends Roo.bootstrap.nav.Simplebar
5825  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5826  * Bootstrap Sidebar class
5827  *
5828  * @cfg {String} brand what is brand
5829  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5830  * @cfg {String} brand_href href of the brand
5831  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5832  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5833  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5834  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5835  * 
5836  * @constructor
5837  * Create a new Sidebar
5838  * @param {Object} config The config object
5839  */
5840
5841
5842 Roo.bootstrap.nav.Headerbar = function(config){
5843     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5844       
5845 };
5846
5847 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5848     
5849     position: '',
5850     brand: '',
5851     brand_href: false,
5852     srButton : true,
5853     autohide : false,
5854     desktopCenter : false,
5855    
5856     
5857     getAutoCreate : function(){
5858         
5859         var   cfg = {
5860             tag: this.nav || 'nav',
5861             cls: 'navbar navbar-expand-md',
5862             role: 'navigation',
5863             cn: []
5864         };
5865         
5866         var cn = cfg.cn;
5867         if (this.desktopCenter) {
5868             cn.push({cls : 'container', cn : []});
5869             cn = cn[0].cn;
5870         }
5871         
5872         if(this.srButton){
5873             var btn = {
5874                 tag: 'button',
5875                 type: 'button',
5876                 cls: 'navbar-toggle navbar-toggler',
5877                 'data-toggle': 'collapse',
5878                 cn: [
5879                     {
5880                         tag: 'span',
5881                         cls: 'sr-only',
5882                         html: 'Toggle navigation'
5883                     },
5884                     {
5885                         tag: 'span',
5886                         cls: 'icon-bar navbar-toggler-icon'
5887                     },
5888                     {
5889                         tag: 'span',
5890                         cls: 'icon-bar'
5891                     },
5892                     {
5893                         tag: 'span',
5894                         cls: 'icon-bar'
5895                     }
5896                 ]
5897             };
5898             
5899             cn.push( Roo.bootstrap.version == 4 ? btn : {
5900                 tag: 'div',
5901                 cls: 'navbar-header',
5902                 cn: [
5903                     btn
5904                 ]
5905             });
5906         }
5907         
5908         cn.push({
5909             tag: 'div',
5910             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5911             cn : []
5912         });
5913         
5914         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5915         
5916         if (['light','white'].indexOf(this.weight) > -1) {
5917             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5918         }
5919         cfg.cls += ' bg-' + this.weight;
5920         
5921         
5922         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5923             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5924             
5925             // tag can override this..
5926             
5927             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5928         }
5929         
5930         if (this.brand !== '') {
5931             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5932             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5933                 tag: 'a',
5934                 href: this.brand_href ? this.brand_href : '#',
5935                 cls: 'navbar-brand',
5936                 cn: [
5937                 this.brand
5938                 ]
5939             });
5940         }
5941         
5942         if(this.main){
5943             cfg.cls += ' main-nav';
5944         }
5945         
5946         
5947         return cfg;
5948
5949         
5950     },
5951     getHeaderChildContainer : function()
5952     {
5953         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5954             return this.el.select('.navbar-header',true).first();
5955         }
5956         
5957         return this.getChildContainer();
5958     },
5959     
5960     getChildContainer : function()
5961     {
5962          
5963         return this.el.select('.roo-navbar-collapse',true).first();
5964          
5965         
5966     },
5967     
5968     initEvents : function()
5969     {
5970         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5971         
5972         if (this.autohide) {
5973             
5974             var prevScroll = 0;
5975             var ft = this.el;
5976             
5977             Roo.get(document).on('scroll',function(e) {
5978                 var ns = Roo.get(document).getScroll().top;
5979                 var os = prevScroll;
5980                 prevScroll = ns;
5981                 
5982                 if(ns > os){
5983                     ft.removeClass('slideDown');
5984                     ft.addClass('slideUp');
5985                     return;
5986                 }
5987                 ft.removeClass('slideUp');
5988                 ft.addClass('slideDown');
5989                  
5990               
5991           },this);
5992         }
5993     }    
5994     
5995 });
5996
5997
5998
5999  
6000
6001  /*
6002  * - LGPL
6003  *
6004  * navbar
6005  * 
6006  */
6007
6008 /**
6009  * @class Roo.bootstrap.nav.Sidebar
6010  * @extends Roo.bootstrap.nav.Bar
6011  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6012  * Bootstrap Sidebar class
6013  * 
6014  * @constructor
6015  * Create a new Sidebar
6016  * @param {Object} config The config object
6017  */
6018
6019
6020 Roo.bootstrap.nav.Sidebar = function(config){
6021     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6022 };
6023
6024 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6025     
6026     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6027     
6028     getAutoCreate : function(){
6029         
6030         
6031         return  {
6032             tag: 'div',
6033             cls: 'sidebar sidebar-nav'
6034         };
6035     
6036         
6037     }
6038     
6039     
6040     
6041 });
6042
6043
6044
6045  
6046
6047  /*
6048  * - LGPL
6049  *
6050  * nav group
6051  * 
6052  */
6053
6054 /**
6055  * @class Roo.bootstrap.nav.Group
6056  * @extends Roo.bootstrap.Component
6057  * @children Roo.bootstrap.nav.Item
6058  * Bootstrap NavGroup class
6059  * @cfg {String} align (left|right)
6060  * @cfg {Boolean} inverse
6061  * @cfg {String} type (nav|pills|tab) default nav
6062  * @cfg {String} navId - reference Id for navbar.
6063  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6064  * 
6065  * @constructor
6066  * Create a new nav group
6067  * @param {Object} config The config object
6068  */
6069
6070 Roo.bootstrap.nav.Group = function(config){
6071     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6072     this.navItems = [];
6073    
6074     Roo.bootstrap.nav.Group.register(this);
6075      this.addEvents({
6076         /**
6077              * @event changed
6078              * Fires when the active item changes
6079              * @param {Roo.bootstrap.nav.Group} this
6080              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6081              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6082          */
6083         'changed': true
6084      });
6085     
6086 };
6087
6088 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6089     
6090     align: '',
6091     inverse: false,
6092     form: false,
6093     type: 'nav',
6094     navId : '',
6095     // private
6096     pilltype : true,
6097     
6098     navItems : false, 
6099     
6100     getAutoCreate : function()
6101     {
6102         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6103         
6104         cfg = {
6105             tag : 'ul',
6106             cls: 'nav' 
6107         };
6108         if (Roo.bootstrap.version == 4) {
6109             if (['tabs','pills'].indexOf(this.type) != -1) {
6110                 cfg.cls += ' nav-' + this.type; 
6111             } else {
6112                 // trying to remove so header bar can right align top?
6113                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6114                     // do not use on header bar... 
6115                     cfg.cls += ' navbar-nav';
6116                 }
6117             }
6118             
6119         } else {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type
6122             } else {
6123                 if (this.type !== 'nav') {
6124                     Roo.log('nav type must be nav/tabs/pills')
6125                 }
6126                 cfg.cls += ' navbar-nav'
6127             }
6128         }
6129         
6130         if (this.parent() && this.parent().sidebar) {
6131             cfg = {
6132                 tag: 'ul',
6133                 cls: 'dashboard-menu sidebar-menu'
6134             };
6135             
6136             return cfg;
6137         }
6138         
6139         if (this.form === true) {
6140             cfg = {
6141                 tag: 'form',
6142                 cls: 'navbar-form form-inline'
6143             };
6144             //nav navbar-right ml-md-auto
6145             if (this.align === 'right') {
6146                 cfg.cls += ' navbar-right ml-md-auto';
6147             } else {
6148                 cfg.cls += ' navbar-left';
6149             }
6150         }
6151         
6152         if (this.align === 'right') {
6153             cfg.cls += ' navbar-right ml-md-auto';
6154         } else {
6155             cfg.cls += ' mr-auto';
6156         }
6157         
6158         if (this.inverse) {
6159             cfg.cls += ' navbar-inverse';
6160             
6161         }
6162         
6163         
6164         return cfg;
6165     },
6166     /**
6167     * sets the active Navigation item
6168     * @param {Roo.bootstrap.nav.Item} the new current navitem
6169     */
6170     setActiveItem : function(item)
6171     {
6172         var prev = false;
6173         Roo.each(this.navItems, function(v){
6174             if (v == item) {
6175                 return ;
6176             }
6177             if (v.isActive()) {
6178                 v.setActive(false, true);
6179                 prev = v;
6180                 
6181             }
6182             
6183         });
6184
6185         item.setActive(true, true);
6186         this.fireEvent('changed', this, item, prev);
6187         
6188         
6189     },
6190     /**
6191     * gets the active Navigation item
6192     * @return {Roo.bootstrap.nav.Item} the current navitem
6193     */
6194     getActive : function()
6195     {
6196         
6197         var prev = false;
6198         Roo.each(this.navItems, function(v){
6199             
6200             if (v.isActive()) {
6201                 prev = v;
6202                 
6203             }
6204             
6205         });
6206         return prev;
6207     },
6208     
6209     indexOfNav : function()
6210     {
6211         
6212         var prev = false;
6213         Roo.each(this.navItems, function(v,i){
6214             
6215             if (v.isActive()) {
6216                 prev = i;
6217                 
6218             }
6219             
6220         });
6221         return prev;
6222     },
6223     /**
6224     * adds a Navigation item
6225     * @param {Roo.bootstrap.nav.Item} the navitem to add
6226     */
6227     addItem : function(cfg)
6228     {
6229         if (this.form && Roo.bootstrap.version == 4) {
6230             cfg.tag = 'div';
6231         }
6232         var cn = new Roo.bootstrap.nav.Item(cfg);
6233         this.register(cn);
6234         cn.parentId = this.id;
6235         cn.onRender(this.el, null);
6236         return cn;
6237     },
6238     /**
6239     * register a Navigation item
6240     * @param {Roo.bootstrap.nav.Item} the navitem to add
6241     */
6242     register : function(item)
6243     {
6244         this.navItems.push( item);
6245         item.navId = this.navId;
6246     
6247     },
6248     
6249     /**
6250     * clear all the Navigation item
6251     */
6252    
6253     clearAll : function()
6254     {
6255         this.navItems = [];
6256         this.el.dom.innerHTML = '';
6257     },
6258     
6259     getNavItem: function(tabId)
6260     {
6261         var ret = false;
6262         Roo.each(this.navItems, function(e) {
6263             if (e.tabId == tabId) {
6264                ret =  e;
6265                return false;
6266             }
6267             return true;
6268             
6269         });
6270         return ret;
6271     },
6272     
6273     setActiveNext : function()
6274     {
6275         var i = this.indexOfNav(this.getActive());
6276         if (i > this.navItems.length) {
6277             return;
6278         }
6279         this.setActiveItem(this.navItems[i+1]);
6280     },
6281     setActivePrev : function()
6282     {
6283         var i = this.indexOfNav(this.getActive());
6284         if (i  < 1) {
6285             return;
6286         }
6287         this.setActiveItem(this.navItems[i-1]);
6288     },
6289     clearWasActive : function(except) {
6290         Roo.each(this.navItems, function(e) {
6291             if (e.tabId != except.tabId && e.was_active) {
6292                e.was_active = false;
6293                return false;
6294             }
6295             return true;
6296             
6297         });
6298     },
6299     getWasActive : function ()
6300     {
6301         var r = false;
6302         Roo.each(this.navItems, function(e) {
6303             if (e.was_active) {
6304                r = e;
6305                return false;
6306             }
6307             return true;
6308             
6309         });
6310         return r;
6311     }
6312     
6313     
6314 });
6315
6316  
6317 Roo.apply(Roo.bootstrap.nav.Group, {
6318     
6319     groups: {},
6320      /**
6321     * register a Navigation Group
6322     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6323     */
6324     register : function(navgrp)
6325     {
6326         this.groups[navgrp.navId] = navgrp;
6327         
6328     },
6329     /**
6330     * fetch a Navigation Group based on the navigation ID
6331     * @param {string} the navgroup to add
6332     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6333     */
6334     get: function(navId) {
6335         if (typeof(this.groups[navId]) == 'undefined') {
6336             return false;
6337             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6338         }
6339         return this.groups[navId] ;
6340     }
6341     
6342     
6343     
6344 });
6345
6346  /**
6347  * @class Roo.bootstrap.nav.Item
6348  * @extends Roo.bootstrap.Component
6349  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6350  * @licence LGPL
6351  * Bootstrap Navbar.NavItem class
6352  * 
6353  * @cfg {String} href  link to
6354  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6355  * @cfg {Boolean} button_outline show and outlined button
6356  * @cfg {String} html content of button
6357  * @cfg {String} badge text inside badge
6358  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6359  * @cfg {String} glyphicon DEPRICATED - use fa
6360  * @cfg {String} icon DEPRICATED - use fa
6361  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6362  * @cfg {Boolean} active Is item active
6363  * @cfg {Boolean} disabled Is item disabled
6364  * @cfg {String} linkcls  Link Class
6365  * @cfg {Boolean} preventDefault (true | false) default false
6366  * @cfg {String} tabId the tab that this item activates.
6367  * @cfg {String} tagtype (a|span) render as a href or span?
6368  * @cfg {Boolean} animateRef (true|false) link to element default false  
6369  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6370   
6371  * @constructor
6372  * Create a new Navbar Item
6373  * @param {Object} config The config object
6374  */
6375 Roo.bootstrap.nav.Item = function(config){
6376     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6377     this.addEvents({
6378         // raw events
6379         /**
6380          * @event click
6381          * The raw click event for the entire grid.
6382          * @param {Roo.EventObject} e
6383          */
6384         "click" : true,
6385          /**
6386             * @event changed
6387             * Fires when the active item active state changes
6388             * @param {Roo.bootstrap.nav.Item} this
6389             * @param {boolean} state the new state
6390              
6391          */
6392         'changed': true,
6393         /**
6394             * @event scrollto
6395             * Fires when scroll to element
6396             * @param {Roo.bootstrap.nav.Item} this
6397             * @param {Object} options
6398             * @param {Roo.EventObject} e
6399              
6400          */
6401         'scrollto': true
6402     });
6403    
6404 };
6405
6406 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6407     
6408     href: false,
6409     html: '',
6410     badge: '',
6411     icon: false,
6412     fa : false,
6413     glyphicon: false,
6414     active: false,
6415     preventDefault : false,
6416     tabId : false,
6417     tagtype : 'a',
6418     tag: 'li',
6419     disabled : false,
6420     animateRef : false,
6421     was_active : false,
6422     button_weight : '',
6423     button_outline : false,
6424     linkcls : '',
6425     navLink: false,
6426     
6427     getAutoCreate : function(){
6428          
6429         var cfg = {
6430             tag: this.tag,
6431             cls: 'nav-item'
6432         };
6433         
6434         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6435         
6436         if (this.active) {
6437             cfg.cls +=  ' active' ;
6438         }
6439         if (this.disabled) {
6440             cfg.cls += ' disabled';
6441         }
6442         
6443         // BS4 only?
6444         if (this.button_weight.length) {
6445             cfg.tag = this.href ? 'a' : 'button';
6446             cfg.html = this.html || '';
6447             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6448             if (this.href) {
6449                 cfg.href = this.href;
6450             }
6451             if (this.fa) {
6452                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6453             } else {
6454                 cfg.cls += " nav-html";
6455             }
6456             
6457             // menu .. should add dropdown-menu class - so no need for carat..
6458             
6459             if (this.badge !== '') {
6460                  
6461                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6462             }
6463             return cfg;
6464         }
6465         
6466         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6467             cfg.cn = [
6468                 {
6469                     tag: this.tagtype,
6470                     href : this.href || "#",
6471                     html: this.html || '',
6472                     cls : ''
6473                 }
6474             ];
6475             if (this.tagtype == 'a') {
6476                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6477         
6478             }
6479             if (this.icon) {
6480                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6481             } else  if (this.fa) {
6482                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483             } else if(this.glyphicon) {
6484                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6485             } else {
6486                 cfg.cn[0].cls += " nav-html";
6487             }
6488             
6489             if (this.menu) {
6490                 cfg.cn[0].html += " <span class='caret'></span>";
6491              
6492             }
6493             
6494             if (this.badge !== '') {
6495                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6496             }
6497         }
6498         
6499         
6500         
6501         return cfg;
6502     },
6503     onRender : function(ct, position)
6504     {
6505        // Roo.log("Call onRender: " + this.xtype);
6506         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6507             this.tag = 'div';
6508         }
6509         
6510         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6511         this.navLink = this.el.select('.nav-link',true).first();
6512         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6513         return ret;
6514     },
6515       
6516     
6517     initEvents: function() 
6518     {
6519         if (typeof (this.menu) != 'undefined') {
6520             this.menu.parentType = this.xtype;
6521             this.menu.triggerEl = this.el;
6522             this.menu = this.addxtype(Roo.apply({}, this.menu));
6523         }
6524         
6525         this.el.on('click', this.onClick, this);
6526         
6527         //if(this.tagtype == 'span'){
6528         //    this.el.select('span',true).on('click', this.onClick, this);
6529         //}
6530        
6531         // at this point parent should be available..
6532         this.parent().register(this);
6533     },
6534     
6535     onClick : function(e)
6536     {
6537         if (e.getTarget('.dropdown-menu-item')) {
6538             // did you click on a menu itemm.... - then don't trigger onclick..
6539             return;
6540         }
6541         
6542         if(
6543                 this.preventDefault || 
6544                 this.href == '#' 
6545         ){
6546             Roo.log("NavItem - prevent Default?");
6547             e.preventDefault();
6548         }
6549         
6550         if (this.disabled) {
6551             return;
6552         }
6553         
6554         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6555         if (tg && tg.transition) {
6556             Roo.log("waiting for the transitionend");
6557             return;
6558         }
6559         
6560         
6561         
6562         //Roo.log("fire event clicked");
6563         if(this.fireEvent('click', this, e) === false){
6564             return;
6565         };
6566         
6567         if(this.tagtype == 'span'){
6568             return;
6569         }
6570         
6571         //Roo.log(this.href);
6572         var ael = this.el.select('a',true).first();
6573         //Roo.log(ael);
6574         
6575         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6576             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6577             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6578                 return; // ignore... - it's a 'hash' to another page.
6579             }
6580             Roo.log("NavItem - prevent Default?");
6581             e.preventDefault();
6582             this.scrollToElement(e);
6583         }
6584         
6585         
6586         var p =  this.parent();
6587    
6588         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6589             if (typeof(p.setActiveItem) !== 'undefined') {
6590                 p.setActiveItem(this);
6591             }
6592         }
6593         
6594         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6595         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6596             // remove the collapsed menu expand...
6597             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6598         }
6599     },
6600     
6601     isActive: function () {
6602         return this.active
6603     },
6604     setActive : function(state, fire, is_was_active)
6605     {
6606         if (this.active && !state && this.navId) {
6607             this.was_active = true;
6608             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6609             if (nv) {
6610                 nv.clearWasActive(this);
6611             }
6612             
6613         }
6614         this.active = state;
6615         
6616         if (!state ) {
6617             this.el.removeClass('active');
6618             this.navLink ? this.navLink.removeClass('active') : false;
6619         } else if (!this.el.hasClass('active')) {
6620             
6621             this.el.addClass('active');
6622             if (Roo.bootstrap.version == 4 && this.navLink ) {
6623                 this.navLink.addClass('active');
6624             }
6625             
6626         }
6627         if (fire) {
6628             this.fireEvent('changed', this, state);
6629         }
6630         
6631         // show a panel if it's registered and related..
6632         
6633         if (!this.navId || !this.tabId || !state || is_was_active) {
6634             return;
6635         }
6636         
6637         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6638         if (!tg) {
6639             return;
6640         }
6641         var pan = tg.getPanelByName(this.tabId);
6642         if (!pan) {
6643             return;
6644         }
6645         // if we can not flip to new panel - go back to old nav highlight..
6646         if (false == tg.showPanel(pan)) {
6647             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6648             if (nv) {
6649                 var onav = nv.getWasActive();
6650                 if (onav) {
6651                     onav.setActive(true, false, true);
6652                 }
6653             }
6654             
6655         }
6656         
6657         
6658         
6659     },
6660      // this should not be here...
6661     setDisabled : function(state)
6662     {
6663         this.disabled = state;
6664         if (!state ) {
6665             this.el.removeClass('disabled');
6666         } else if (!this.el.hasClass('disabled')) {
6667             this.el.addClass('disabled');
6668         }
6669         
6670     },
6671     
6672     /**
6673      * Fetch the element to display the tooltip on.
6674      * @return {Roo.Element} defaults to this.el
6675      */
6676     tooltipEl : function()
6677     {
6678         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6679     },
6680     
6681     scrollToElement : function(e)
6682     {
6683         var c = document.body;
6684         
6685         /*
6686          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6687          */
6688         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6689             c = document.documentElement;
6690         }
6691         
6692         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6693         
6694         if(!target){
6695             return;
6696         }
6697
6698         var o = target.calcOffsetsTo(c);
6699         
6700         var options = {
6701             target : target,
6702             value : o[1]
6703         };
6704         
6705         this.fireEvent('scrollto', this, options, e);
6706         
6707         Roo.get(c).scrollTo('top', options.value, true);
6708         
6709         return;
6710     },
6711     /**
6712      * Set the HTML (text content) of the item
6713      * @param {string} html  content for the nav item
6714      */
6715     setHtml : function(html)
6716     {
6717         this.html = html;
6718         this.htmlEl.dom.innerHTML = html;
6719         
6720     } 
6721 });
6722  
6723
6724  /*
6725  * - LGPL
6726  *
6727  * sidebar item
6728  *
6729  *  li
6730  *    <span> icon </span>
6731  *    <span> text </span>
6732  *    <span>badge </span>
6733  */
6734
6735 /**
6736  * @class Roo.bootstrap.nav.SidebarItem
6737  * @extends Roo.bootstrap.nav.Item
6738  * Bootstrap Navbar.NavSidebarItem class
6739  * 
6740  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6741  * {Boolean} open is the menu open
6742  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6743  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6744  * {String} buttonSize (sm|md|lg)the extra classes for the button
6745  * {Boolean} showArrow show arrow next to the text (default true)
6746  * @constructor
6747  * Create a new Navbar Button
6748  * @param {Object} config The config object
6749  */
6750 Roo.bootstrap.nav.SidebarItem = function(config){
6751     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6752     this.addEvents({
6753         // raw events
6754         /**
6755          * @event click
6756          * The raw click event for the entire grid.
6757          * @param {Roo.EventObject} e
6758          */
6759         "click" : true,
6760          /**
6761             * @event changed
6762             * Fires when the active item active state changes
6763             * @param {Roo.bootstrap.nav.SidebarItem} this
6764             * @param {boolean} state the new state
6765              
6766          */
6767         'changed': true
6768     });
6769    
6770 };
6771
6772 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6773     
6774     badgeWeight : 'default',
6775     
6776     open: false,
6777     
6778     buttonView : false,
6779     
6780     buttonWeight : 'default',
6781     
6782     buttonSize : 'md',
6783     
6784     showArrow : true,
6785     
6786     getAutoCreate : function(){
6787         
6788         
6789         var a = {
6790                 tag: 'a',
6791                 href : this.href || '#',
6792                 cls: '',
6793                 html : '',
6794                 cn : []
6795         };
6796         
6797         if(this.buttonView){
6798             a = {
6799                 tag: 'button',
6800                 href : this.href || '#',
6801                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6802                 html : this.html,
6803                 cn : []
6804             };
6805         }
6806         
6807         var cfg = {
6808             tag: 'li',
6809             cls: '',
6810             cn: [ a ]
6811         };
6812         
6813         if (this.active) {
6814             cfg.cls += ' active';
6815         }
6816         
6817         if (this.disabled) {
6818             cfg.cls += ' disabled';
6819         }
6820         if (this.open) {
6821             cfg.cls += ' open x-open';
6822         }
6823         // left icon..
6824         if (this.glyphicon || this.icon) {
6825             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6826             a.cn.push({ tag : 'i', cls : c }) ;
6827         }
6828         
6829         if(!this.buttonView){
6830             var span = {
6831                 tag: 'span',
6832                 html : this.html || ''
6833             };
6834
6835             a.cn.push(span);
6836             
6837         }
6838         
6839         if (this.badge !== '') {
6840             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6841         }
6842         
6843         if (this.menu) {
6844             
6845             if(this.showArrow){
6846                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6847             }
6848             
6849             a.cls += ' dropdown-toggle treeview' ;
6850         }
6851         
6852         return cfg;
6853     },
6854     
6855     initEvents : function()
6856     { 
6857         if (typeof (this.menu) != 'undefined') {
6858             this.menu.parentType = this.xtype;
6859             this.menu.triggerEl = this.el;
6860             this.menu = this.addxtype(Roo.apply({}, this.menu));
6861         }
6862         
6863         this.el.on('click', this.onClick, this);
6864         
6865         if(this.badge !== ''){
6866             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6867         }
6868         
6869     },
6870     
6871     onClick : function(e)
6872     {
6873         if(this.disabled){
6874             e.preventDefault();
6875             return;
6876         }
6877         
6878         if(this.preventDefault){
6879             e.preventDefault();
6880         }
6881         
6882         this.fireEvent('click', this, e);
6883     },
6884     
6885     disable : function()
6886     {
6887         this.setDisabled(true);
6888     },
6889     
6890     enable : function()
6891     {
6892         this.setDisabled(false);
6893     },
6894     
6895     setDisabled : function(state)
6896     {
6897         if(this.disabled == state){
6898             return;
6899         }
6900         
6901         this.disabled = state;
6902         
6903         if (state) {
6904             this.el.addClass('disabled');
6905             return;
6906         }
6907         
6908         this.el.removeClass('disabled');
6909         
6910         return;
6911     },
6912     
6913     setActive : function(state)
6914     {
6915         if(this.active == state){
6916             return;
6917         }
6918         
6919         this.active = state;
6920         
6921         if (state) {
6922             this.el.addClass('active');
6923             return;
6924         }
6925         
6926         this.el.removeClass('active');
6927         
6928         return;
6929     },
6930     
6931     isActive: function () 
6932     {
6933         return this.active;
6934     },
6935     
6936     setBadge : function(str)
6937     {
6938         if(!this.badgeEl){
6939             return;
6940         }
6941         
6942         this.badgeEl.dom.innerHTML = str;
6943     }
6944     
6945    
6946      
6947  
6948 });
6949  
6950
6951  /*
6952  * - LGPL
6953  *
6954  * nav progress bar
6955  * 
6956  */
6957
6958 /**
6959  * @class Roo.bootstrap.nav.ProgressBar
6960  * @extends Roo.bootstrap.Component
6961  * @children Roo.bootstrap.nav.ProgressBarItem
6962  * Bootstrap NavProgressBar class
6963  * 
6964  * @constructor
6965  * Create a new nav progress bar - a bar indicating step along a process
6966  * @param {Object} config The config object
6967  */
6968
6969 Roo.bootstrap.nav.ProgressBar = function(config){
6970     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6971
6972     this.bullets = this.bullets || [];
6973    
6974 //    Roo.bootstrap.nav.ProgressBar.register(this);
6975      this.addEvents({
6976         /**
6977              * @event changed
6978              * Fires when the active item changes
6979              * @param {Roo.bootstrap.nav.ProgressBar} this
6980              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6981              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6982          */
6983         'changed': true
6984      });
6985     
6986 };
6987
6988 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6989     /**
6990      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6991      * Bullets for the Nav Progress bar for the toolbar
6992      */
6993     bullets : [],
6994     barItems : [],
6995     
6996     getAutoCreate : function()
6997     {
6998         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
6999         
7000         cfg = {
7001             tag : 'div',
7002             cls : 'roo-navigation-bar-group',
7003             cn : [
7004                 {
7005                     tag : 'div',
7006                     cls : 'roo-navigation-top-bar'
7007                 },
7008                 {
7009                     tag : 'div',
7010                     cls : 'roo-navigation-bullets-bar',
7011                     cn : [
7012                         {
7013                             tag : 'ul',
7014                             cls : 'roo-navigation-bar'
7015                         }
7016                     ]
7017                 },
7018                 
7019                 {
7020                     tag : 'div',
7021                     cls : 'roo-navigation-bottom-bar'
7022                 }
7023             ]
7024             
7025         };
7026         
7027         return cfg;
7028         
7029     },
7030     
7031     initEvents: function() 
7032     {
7033         
7034     },
7035     
7036     onRender : function(ct, position) 
7037     {
7038         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7039         
7040         if(this.bullets.length){
7041             Roo.each(this.bullets, function(b){
7042                this.addItem(b);
7043             }, this);
7044         }
7045         
7046         this.format();
7047         
7048     },
7049     
7050     addItem : function(cfg)
7051     {
7052         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7053         
7054         item.parentId = this.id;
7055         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7056         
7057         if(cfg.html){
7058             var top = new Roo.bootstrap.Element({
7059                 tag : 'div',
7060                 cls : 'roo-navigation-bar-text'
7061             });
7062             
7063             var bottom = new Roo.bootstrap.Element({
7064                 tag : 'div',
7065                 cls : 'roo-navigation-bar-text'
7066             });
7067             
7068             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7069             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7070             
7071             var topText = new Roo.bootstrap.Element({
7072                 tag : 'span',
7073                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7074             });
7075             
7076             var bottomText = new Roo.bootstrap.Element({
7077                 tag : 'span',
7078                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7079             });
7080             
7081             topText.onRender(top.el, null);
7082             bottomText.onRender(bottom.el, null);
7083             
7084             item.topEl = top;
7085             item.bottomEl = bottom;
7086         }
7087         
7088         this.barItems.push(item);
7089         
7090         return item;
7091     },
7092     
7093     getActive : function()
7094     {
7095         var active = false;
7096         
7097         Roo.each(this.barItems, function(v){
7098             
7099             if (!v.isActive()) {
7100                 return;
7101             }
7102             
7103             active = v;
7104             return false;
7105             
7106         });
7107         
7108         return active;
7109     },
7110     
7111     setActiveItem : function(item)
7112     {
7113         var prev = false;
7114         
7115         Roo.each(this.barItems, function(v){
7116             if (v.rid == item.rid) {
7117                 return ;
7118             }
7119             
7120             if (v.isActive()) {
7121                 v.setActive(false);
7122                 prev = v;
7123             }
7124         });
7125
7126         item.setActive(true);
7127         
7128         this.fireEvent('changed', this, item, prev);
7129     },
7130     
7131     getBarItem: function(rid)
7132     {
7133         var ret = false;
7134         
7135         Roo.each(this.barItems, function(e) {
7136             if (e.rid != rid) {
7137                 return;
7138             }
7139             
7140             ret =  e;
7141             return false;
7142         });
7143         
7144         return ret;
7145     },
7146     
7147     indexOfItem : function(item)
7148     {
7149         var index = false;
7150         
7151         Roo.each(this.barItems, function(v, i){
7152             
7153             if (v.rid != item.rid) {
7154                 return;
7155             }
7156             
7157             index = i;
7158             return false
7159         });
7160         
7161         return index;
7162     },
7163     
7164     setActiveNext : function()
7165     {
7166         var i = this.indexOfItem(this.getActive());
7167         
7168         if (i > this.barItems.length) {
7169             return;
7170         }
7171         
7172         this.setActiveItem(this.barItems[i+1]);
7173     },
7174     
7175     setActivePrev : function()
7176     {
7177         var i = this.indexOfItem(this.getActive());
7178         
7179         if (i  < 1) {
7180             return;
7181         }
7182         
7183         this.setActiveItem(this.barItems[i-1]);
7184     },
7185     
7186     format : function()
7187     {
7188         if(!this.barItems.length){
7189             return;
7190         }
7191      
7192         var width = 100 / this.barItems.length;
7193         
7194         Roo.each(this.barItems, function(i){
7195             i.el.setStyle('width', width + '%');
7196             i.topEl.el.setStyle('width', width + '%');
7197             i.bottomEl.el.setStyle('width', width + '%');
7198         }, this);
7199         
7200     }
7201     
7202 });
7203 /*
7204  * - LGPL
7205  *
7206  * Nav Progress Item
7207  * 
7208  */
7209
7210 /**
7211  * @class Roo.bootstrap.nav.ProgressBarItem
7212  * @extends Roo.bootstrap.Component
7213  * Bootstrap NavProgressBarItem class
7214  * @cfg {String} rid the reference id
7215  * @cfg {Boolean} active (true|false) Is item active default false
7216  * @cfg {Boolean} disabled (true|false) Is item active default false
7217  * @cfg {String} html
7218  * @cfg {String} position (top|bottom) text position default bottom
7219  * @cfg {String} icon show icon instead of number
7220  * 
7221  * @constructor
7222  * Create a new NavProgressBarItem
7223  * @param {Object} config The config object
7224  */
7225 Roo.bootstrap.nav.ProgressBarItem = function(config){
7226     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7227     this.addEvents({
7228         // raw events
7229         /**
7230          * @event click
7231          * The raw click event for the entire grid.
7232          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7233          * @param {Roo.EventObject} e
7234          */
7235         "click" : true
7236     });
7237    
7238 };
7239
7240 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7241     
7242     rid : '',
7243     active : false,
7244     disabled : false,
7245     html : '',
7246     position : 'bottom',
7247     icon : false,
7248     
7249     getAutoCreate : function()
7250     {
7251         var iconCls = 'roo-navigation-bar-item-icon';
7252         
7253         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7254         
7255         var cfg = {
7256             tag: 'li',
7257             cls: 'roo-navigation-bar-item',
7258             cn : [
7259                 {
7260                     tag : 'i',
7261                     cls : iconCls
7262                 }
7263             ]
7264         };
7265         
7266         if(this.active){
7267             cfg.cls += ' active';
7268         }
7269         if(this.disabled){
7270             cfg.cls += ' disabled';
7271         }
7272         
7273         return cfg;
7274     },
7275     
7276     disable : function()
7277     {
7278         this.setDisabled(true);
7279     },
7280     
7281     enable : function()
7282     {
7283         this.setDisabled(false);
7284     },
7285     
7286     initEvents: function() 
7287     {
7288         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7289         
7290         this.iconEl.on('click', this.onClick, this);
7291     },
7292     
7293     onClick : function(e)
7294     {
7295         e.preventDefault();
7296         
7297         if(this.disabled){
7298             return;
7299         }
7300         
7301         if(this.fireEvent('click', this, e) === false){
7302             return;
7303         };
7304         
7305         this.parent().setActiveItem(this);
7306     },
7307     
7308     isActive: function () 
7309     {
7310         return this.active;
7311     },
7312     
7313     setActive : function(state)
7314     {
7315         if(this.active == state){
7316             return;
7317         }
7318         
7319         this.active = state;
7320         
7321         if (state) {
7322             this.el.addClass('active');
7323             return;
7324         }
7325         
7326         this.el.removeClass('active');
7327         
7328         return;
7329     },
7330     
7331     setDisabled : function(state)
7332     {
7333         if(this.disabled == state){
7334             return;
7335         }
7336         
7337         this.disabled = state;
7338         
7339         if (state) {
7340             this.el.addClass('disabled');
7341             return;
7342         }
7343         
7344         this.el.removeClass('disabled');
7345     },
7346     
7347     tooltipEl : function()
7348     {
7349         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7350     }
7351 });
7352  
7353
7354  /*
7355  * - LGPL
7356  *
7357  *  Breadcrumb Nav
7358  * 
7359  */
7360 Roo.namespace('Roo.bootstrap.breadcrumb');
7361
7362
7363 /**
7364  * @class Roo.bootstrap.breadcrumb.Nav
7365  * @extends Roo.bootstrap.Component
7366  * Bootstrap Breadcrumb Nav Class
7367  *  
7368  * @children Roo.bootstrap.breadcrumb.Item
7369  * 
7370  * @constructor
7371  * Create a new breadcrumb.Nav
7372  * @param {Object} config The config object
7373  */
7374
7375
7376 Roo.bootstrap.breadcrumb.Nav = function(config){
7377     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7378     
7379     
7380 };
7381
7382 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7383     
7384     getAutoCreate : function()
7385     {
7386
7387         var cfg = {
7388             tag: 'nav',
7389             cn : [
7390                 {
7391                     tag : 'ol',
7392                     cls : 'breadcrumb'
7393                 }
7394             ]
7395             
7396         };
7397           
7398         return cfg;
7399     },
7400     
7401     initEvents: function()
7402     {
7403         this.olEl = this.el.select('ol',true).first();    
7404     },
7405     getChildContainer : function()
7406     {
7407         return this.olEl;  
7408     }
7409     
7410 });
7411
7412  /*
7413  * - LGPL
7414  *
7415  *  Breadcrumb Item
7416  * 
7417  */
7418
7419
7420 /**
7421  * @class Roo.bootstrap.breadcrumb.Nav
7422  * @extends Roo.bootstrap.Component
7423  * @children Roo.bootstrap.Component
7424  * Bootstrap Breadcrumb Nav Class
7425  *  
7426  * 
7427  * @cfg {String} html the content of the link.
7428  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7429  * @cfg {Boolean} active is it active
7430
7431  * 
7432  * @constructor
7433  * Create a new breadcrumb.Nav
7434  * @param {Object} config The config object
7435  */
7436
7437 Roo.bootstrap.breadcrumb.Item = function(config){
7438     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7439     this.addEvents({
7440         // img events
7441         /**
7442          * @event click
7443          * The img click event for the img.
7444          * @param {Roo.EventObject} e
7445          */
7446         "click" : true
7447     });
7448     
7449 };
7450
7451 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7452     
7453     href: false,
7454     html : '',
7455     
7456     getAutoCreate : function()
7457     {
7458
7459         var cfg = {
7460             tag: 'li',
7461             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7462         };
7463         if (this.href !== false) {
7464             cfg.cn = [{
7465                 tag : 'a',
7466                 href : this.href,
7467                 html : this.html
7468             }];
7469         } else {
7470             cfg.html = this.html;
7471         }
7472         
7473         return cfg;
7474     },
7475     
7476     initEvents: function()
7477     {
7478         if (this.href) {
7479             this.el.select('a', true).first().on('click',this.onClick, this)
7480         }
7481         
7482     },
7483     onClick : function(e)
7484     {
7485         e.preventDefault();
7486         this.fireEvent('click',this,  e);
7487     }
7488     
7489 });
7490
7491  /*
7492  * - LGPL
7493  *
7494  * row
7495  * 
7496  */
7497
7498 /**
7499  * @class Roo.bootstrap.Row
7500  * @extends Roo.bootstrap.Component
7501  * @children Roo.bootstrap.Component
7502  * Bootstrap Row class (contains columns...)
7503  * 
7504  * @constructor
7505  * Create a new Row
7506  * @param {Object} config The config object
7507  */
7508
7509 Roo.bootstrap.Row = function(config){
7510     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7511 };
7512
7513 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7514     
7515     getAutoCreate : function(){
7516        return {
7517             cls: 'row clearfix'
7518        };
7519     }
7520     
7521     
7522 });
7523
7524  
7525
7526  /*
7527  * - LGPL
7528  *
7529  * pagination
7530  * 
7531  */
7532
7533 /**
7534  * @class Roo.bootstrap.Pagination
7535  * @extends Roo.bootstrap.Component
7536  * @children Roo.bootstrap.Pagination
7537  * Bootstrap Pagination class
7538  * 
7539  * @cfg {String} size (xs|sm|md|lg|xl)
7540  * @cfg {Boolean} inverse 
7541  * 
7542  * @constructor
7543  * Create a new Pagination
7544  * @param {Object} config The config object
7545  */
7546
7547 Roo.bootstrap.Pagination = function(config){
7548     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7549 };
7550
7551 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7552     
7553     cls: false,
7554     size: false,
7555     inverse: false,
7556     
7557     getAutoCreate : function(){
7558         var cfg = {
7559             tag: 'ul',
7560                 cls: 'pagination'
7561         };
7562         if (this.inverse) {
7563             cfg.cls += ' inverse';
7564         }
7565         if (this.html) {
7566             cfg.html=this.html;
7567         }
7568         if (this.cls) {
7569             cfg.cls += " " + this.cls;
7570         }
7571         return cfg;
7572     }
7573    
7574 });
7575
7576  
7577
7578  /*
7579  * - LGPL
7580  *
7581  * Pagination item
7582  * 
7583  */
7584
7585
7586 /**
7587  * @class Roo.bootstrap.PaginationItem
7588  * @extends Roo.bootstrap.Component
7589  * Bootstrap PaginationItem class
7590  * @cfg {String} html text
7591  * @cfg {String} href the link
7592  * @cfg {Boolean} preventDefault (true | false) default true
7593  * @cfg {Boolean} active (true | false) default false
7594  * @cfg {Boolean} disabled default false
7595  * 
7596  * 
7597  * @constructor
7598  * Create a new PaginationItem
7599  * @param {Object} config The config object
7600  */
7601
7602
7603 Roo.bootstrap.PaginationItem = function(config){
7604     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7605     this.addEvents({
7606         // raw events
7607         /**
7608          * @event click
7609          * The raw click event for the entire grid.
7610          * @param {Roo.EventObject} e
7611          */
7612         "click" : true
7613     });
7614 };
7615
7616 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7617     
7618     href : false,
7619     html : false,
7620     preventDefault: true,
7621     active : false,
7622     cls : false,
7623     disabled: false,
7624     
7625     getAutoCreate : function(){
7626         var cfg= {
7627             tag: 'li',
7628             cn: [
7629                 {
7630                     tag : 'a',
7631                     href : this.href ? this.href : '#',
7632                     html : this.html ? this.html : ''
7633                 }
7634             ]
7635         };
7636         
7637         if(this.cls){
7638             cfg.cls = this.cls;
7639         }
7640         
7641         if(this.disabled){
7642             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7643         }
7644         
7645         if(this.active){
7646             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7647         }
7648         
7649         return cfg;
7650     },
7651     
7652     initEvents: function() {
7653         
7654         this.el.on('click', this.onClick, this);
7655         
7656     },
7657     onClick : function(e)
7658     {
7659         Roo.log('PaginationItem on click ');
7660         if(this.preventDefault){
7661             e.preventDefault();
7662         }
7663         
7664         if(this.disabled){
7665             return;
7666         }
7667         
7668         this.fireEvent('click', this, e);
7669     }
7670    
7671 });
7672
7673  
7674
7675  /*
7676  * - LGPL
7677  *
7678  * slider
7679  * 
7680  */
7681
7682
7683 /**
7684  * @class Roo.bootstrap.Slider
7685  * @extends Roo.bootstrap.Component
7686  * Bootstrap Slider class
7687  *    
7688  * @constructor
7689  * Create a new Slider
7690  * @param {Object} config The config object
7691  */
7692
7693 Roo.bootstrap.Slider = function(config){
7694     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7695 };
7696
7697 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7698     
7699     getAutoCreate : function(){
7700         
7701         var cfg = {
7702             tag: 'div',
7703             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7704             cn: [
7705                 {
7706                     tag: 'a',
7707                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7708                 }
7709             ]
7710         };
7711         
7712         return cfg;
7713     }
7714    
7715 });
7716
7717  /*
7718  * Based on:
7719  * Ext JS Library 1.1.1
7720  * Copyright(c) 2006-2007, Ext JS, LLC.
7721  *
7722  * Originally Released Under LGPL - original licence link has changed is not relivant.
7723  *
7724  * Fork - LGPL
7725  * <script type="text/javascript">
7726  */
7727  /**
7728  * @extends Roo.dd.DDProxy
7729  * @class Roo.grid.SplitDragZone
7730  * Support for Column Header resizing
7731  * @constructor
7732  * @param {Object} config
7733  */
7734 // private
7735 // This is a support class used internally by the Grid components
7736 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7737     this.grid = grid;
7738     this.view = grid.getView();
7739     this.proxy = this.view.resizeProxy;
7740     Roo.grid.SplitDragZone.superclass.constructor.call(
7741         this,
7742         hd, // ID
7743         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7744         {  // CONFIG
7745             dragElId : Roo.id(this.proxy.dom),
7746             resizeFrame:false
7747         }
7748     );
7749     
7750     this.setHandleElId(Roo.id(hd));
7751     if (hd2 !== false) {
7752         this.setOuterHandleElId(Roo.id(hd2));
7753     }
7754     
7755     this.scroll = false;
7756 };
7757 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7758     fly: Roo.Element.fly,
7759
7760     b4StartDrag : function(x, y){
7761         this.view.headersDisabled = true;
7762         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7763                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7764         );
7765         this.proxy.setHeight(h);
7766         
7767         // for old system colWidth really stored the actual width?
7768         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7769         // which in reality did not work.. - it worked only for fixed sizes
7770         // for resizable we need to use actual sizes.
7771         var w = this.cm.getColumnWidth(this.cellIndex);
7772         if (!this.view.mainWrap) {
7773             // bootstrap.
7774             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7775         }
7776         
7777         
7778         
7779         // this was w-this.grid.minColumnWidth;
7780         // doesnt really make sense? - w = thie curren width or the rendered one?
7781         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7782         this.resetConstraints();
7783         this.setXConstraint(minw, 1000);
7784         this.setYConstraint(0, 0);
7785         this.minX = x - minw;
7786         this.maxX = x + 1000;
7787         this.startPos = x;
7788         if (!this.view.mainWrap) { // this is Bootstrap code..
7789             this.getDragEl().style.display='block';
7790         }
7791         
7792         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7793     },
7794
7795
7796     handleMouseDown : function(e){
7797         ev = Roo.EventObject.setEvent(e);
7798         var t = this.fly(ev.getTarget());
7799         if(t.hasClass("x-grid-split")){
7800             this.cellIndex = this.view.getCellIndex(t.dom);
7801             this.split = t.dom;
7802             this.cm = this.grid.colModel;
7803             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7804                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7805             }
7806         }
7807     },
7808
7809     endDrag : function(e){
7810         this.view.headersDisabled = false;
7811         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7812         var diff = endX - this.startPos;
7813         // 
7814         var w = this.cm.getColumnWidth(this.cellIndex);
7815         if (!this.view.mainWrap) {
7816             w = 0;
7817         }
7818         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7819     },
7820
7821     autoOffset : function(){
7822         this.setDelta(0,0);
7823     }
7824 });/*
7825  * Based on:
7826  * Ext JS Library 1.1.1
7827  * Copyright(c) 2006-2007, Ext JS, LLC.
7828  *
7829  * Originally Released Under LGPL - original licence link has changed is not relivant.
7830  *
7831  * Fork - LGPL
7832  * <script type="text/javascript">
7833  */
7834
7835 /**
7836  * @class Roo.grid.AbstractSelectionModel
7837  * @extends Roo.util.Observable
7838  * @abstract
7839  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7840  * implemented by descendant classes.  This class should not be directly instantiated.
7841  * @constructor
7842  */
7843 Roo.grid.AbstractSelectionModel = function(){
7844     this.locked = false;
7845     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7846 };
7847
7848 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7849     /** @ignore Called by the grid automatically. Do not call directly. */
7850     init : function(grid){
7851         this.grid = grid;
7852         this.initEvents();
7853     },
7854
7855     /**
7856      * Locks the selections.
7857      */
7858     lock : function(){
7859         this.locked = true;
7860     },
7861
7862     /**
7863      * Unlocks the selections.
7864      */
7865     unlock : function(){
7866         this.locked = false;
7867     },
7868
7869     /**
7870      * Returns true if the selections are locked.
7871      * @return {Boolean}
7872      */
7873     isLocked : function(){
7874         return this.locked;
7875     }
7876 });/*
7877  * Based on:
7878  * Ext JS Library 1.1.1
7879  * Copyright(c) 2006-2007, Ext JS, LLC.
7880  *
7881  * Originally Released Under LGPL - original licence link has changed is not relivant.
7882  *
7883  * Fork - LGPL
7884  * <script type="text/javascript">
7885  */
7886 /**
7887  * @extends Roo.grid.AbstractSelectionModel
7888  * @class Roo.grid.RowSelectionModel
7889  * The default SelectionModel used by {@link Roo.grid.Grid}.
7890  * It supports multiple selections and keyboard selection/navigation. 
7891  * @constructor
7892  * @param {Object} config
7893  */
7894 Roo.grid.RowSelectionModel = function(config){
7895     Roo.apply(this, config);
7896     this.selections = new Roo.util.MixedCollection(false, function(o){
7897         return o.id;
7898     });
7899
7900     this.last = false;
7901     this.lastActive = false;
7902
7903     this.addEvents({
7904         /**
7905         * @event selectionchange
7906         * Fires when the selection changes
7907         * @param {SelectionModel} this
7908         */
7909        "selectionchange" : true,
7910        /**
7911         * @event afterselectionchange
7912         * Fires after the selection changes (eg. by key press or clicking)
7913         * @param {SelectionModel} this
7914         */
7915        "afterselectionchange" : true,
7916        /**
7917         * @event beforerowselect
7918         * Fires when a row is selected being selected, return false to cancel.
7919         * @param {SelectionModel} this
7920         * @param {Number} rowIndex The selected index
7921         * @param {Boolean} keepExisting False if other selections will be cleared
7922         */
7923        "beforerowselect" : true,
7924        /**
7925         * @event rowselect
7926         * Fires when a row is selected.
7927         * @param {SelectionModel} this
7928         * @param {Number} rowIndex The selected index
7929         * @param {Roo.data.Record} r The record
7930         */
7931        "rowselect" : true,
7932        /**
7933         * @event rowdeselect
7934         * Fires when a row is deselected.
7935         * @param {SelectionModel} this
7936         * @param {Number} rowIndex The selected index
7937         */
7938         "rowdeselect" : true
7939     });
7940     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7941     this.locked = false;
7942 };
7943
7944 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7945     /**
7946      * @cfg {Boolean} singleSelect
7947      * True to allow selection of only one row at a time (defaults to false)
7948      */
7949     singleSelect : false,
7950
7951     // private
7952     initEvents : function(){
7953
7954         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7955             this.grid.on("mousedown", this.handleMouseDown, this);
7956         }else{ // allow click to work like normal
7957             this.grid.on("rowclick", this.handleDragableRowClick, this);
7958         }
7959         // bootstrap does not have a view..
7960         var view = this.grid.view ? this.grid.view : this.grid;
7961         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7962             "up" : function(e){
7963                 if(!e.shiftKey){
7964                     this.selectPrevious(e.shiftKey);
7965                 }else if(this.last !== false && this.lastActive !== false){
7966                     var last = this.last;
7967                     this.selectRange(this.last,  this.lastActive-1);
7968                     view.focusRow(this.lastActive);
7969                     if(last !== false){
7970                         this.last = last;
7971                     }
7972                 }else{
7973                     this.selectFirstRow();
7974                 }
7975                 this.fireEvent("afterselectionchange", this);
7976             },
7977             "down" : function(e){
7978                 if(!e.shiftKey){
7979                     this.selectNext(e.shiftKey);
7980                 }else if(this.last !== false && this.lastActive !== false){
7981                     var last = this.last;
7982                     this.selectRange(this.last,  this.lastActive+1);
7983                     view.focusRow(this.lastActive);
7984                     if(last !== false){
7985                         this.last = last;
7986                     }
7987                 }else{
7988                     this.selectFirstRow();
7989                 }
7990                 this.fireEvent("afterselectionchange", this);
7991             },
7992             scope: this
7993         });
7994
7995          
7996         view.on("refresh", this.onRefresh, this);
7997         view.on("rowupdated", this.onRowUpdated, this);
7998         view.on("rowremoved", this.onRemove, this);
7999     },
8000
8001     // private
8002     onRefresh : function(){
8003         var ds = this.grid.ds, i, v = this.grid.view;
8004         var s = this.selections;
8005         s.each(function(r){
8006             if((i = ds.indexOfId(r.id)) != -1){
8007                 v.onRowSelect(i);
8008                 s.add(ds.getAt(i)); // updating the selection relate data
8009             }else{
8010                 s.remove(r);
8011             }
8012         });
8013     },
8014
8015     // private
8016     onRemove : function(v, index, r){
8017         this.selections.remove(r);
8018     },
8019
8020     // private
8021     onRowUpdated : function(v, index, r){
8022         if(this.isSelected(r)){
8023             v.onRowSelect(index);
8024         }
8025     },
8026
8027     /**
8028      * Select records.
8029      * @param {Array} records The records to select
8030      * @param {Boolean} keepExisting (optional) True to keep existing selections
8031      */
8032     selectRecords : function(records, keepExisting){
8033         if(!keepExisting){
8034             this.clearSelections();
8035         }
8036         var ds = this.grid.ds;
8037         for(var i = 0, len = records.length; i < len; i++){
8038             this.selectRow(ds.indexOf(records[i]), true);
8039         }
8040     },
8041
8042     /**
8043      * Gets the number of selected rows.
8044      * @return {Number}
8045      */
8046     getCount : function(){
8047         return this.selections.length;
8048     },
8049
8050     /**
8051      * Selects the first row in the grid.
8052      */
8053     selectFirstRow : function(){
8054         this.selectRow(0);
8055     },
8056
8057     /**
8058      * Select the last row.
8059      * @param {Boolean} keepExisting (optional) True to keep existing selections
8060      */
8061     selectLastRow : function(keepExisting){
8062         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8063     },
8064
8065     /**
8066      * Selects the row immediately following the last selected row.
8067      * @param {Boolean} keepExisting (optional) True to keep existing selections
8068      */
8069     selectNext : function(keepExisting){
8070         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8071             this.selectRow(this.last+1, keepExisting);
8072             var view = this.grid.view ? this.grid.view : this.grid;
8073             view.focusRow(this.last);
8074         }
8075     },
8076
8077     /**
8078      * Selects the row that precedes the last selected row.
8079      * @param {Boolean} keepExisting (optional) True to keep existing selections
8080      */
8081     selectPrevious : function(keepExisting){
8082         if(this.last){
8083             this.selectRow(this.last-1, keepExisting);
8084             var view = this.grid.view ? this.grid.view : this.grid;
8085             view.focusRow(this.last);
8086         }
8087     },
8088
8089     /**
8090      * Returns the selected records
8091      * @return {Array} Array of selected records
8092      */
8093     getSelections : function(){
8094         return [].concat(this.selections.items);
8095     },
8096
8097     /**
8098      * Returns the first selected record.
8099      * @return {Record}
8100      */
8101     getSelected : function(){
8102         return this.selections.itemAt(0);
8103     },
8104
8105
8106     /**
8107      * Clears all selections.
8108      */
8109     clearSelections : function(fast){
8110         if(this.locked) {
8111             return;
8112         }
8113         if(fast !== true){
8114             var ds = this.grid.ds;
8115             var s = this.selections;
8116             s.each(function(r){
8117                 this.deselectRow(ds.indexOfId(r.id));
8118             }, this);
8119             s.clear();
8120         }else{
8121             this.selections.clear();
8122         }
8123         this.last = false;
8124     },
8125
8126
8127     /**
8128      * Selects all rows.
8129      */
8130     selectAll : function(){
8131         if(this.locked) {
8132             return;
8133         }
8134         this.selections.clear();
8135         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8136             this.selectRow(i, true);
8137         }
8138     },
8139
8140     /**
8141      * Returns True if there is a selection.
8142      * @return {Boolean}
8143      */
8144     hasSelection : function(){
8145         return this.selections.length > 0;
8146     },
8147
8148     /**
8149      * Returns True if the specified row is selected.
8150      * @param {Number/Record} record The record or index of the record to check
8151      * @return {Boolean}
8152      */
8153     isSelected : function(index){
8154         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8155         return (r && this.selections.key(r.id) ? true : false);
8156     },
8157
8158     /**
8159      * Returns True if the specified record id is selected.
8160      * @param {String} id The id of record to check
8161      * @return {Boolean}
8162      */
8163     isIdSelected : function(id){
8164         return (this.selections.key(id) ? true : false);
8165     },
8166
8167     // private
8168     handleMouseDown : function(e, t)
8169     {
8170         var view = this.grid.view ? this.grid.view : this.grid;
8171         var rowIndex;
8172         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8173             return;
8174         };
8175         if(e.shiftKey && this.last !== false){
8176             var last = this.last;
8177             this.selectRange(last, rowIndex, e.ctrlKey);
8178             this.last = last; // reset the last
8179             view.focusRow(rowIndex);
8180         }else{
8181             var isSelected = this.isSelected(rowIndex);
8182             if(e.button !== 0 && isSelected){
8183                 view.focusRow(rowIndex);
8184             }else if(e.ctrlKey && isSelected){
8185                 this.deselectRow(rowIndex);
8186             }else if(!isSelected){
8187                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8188                 view.focusRow(rowIndex);
8189             }
8190         }
8191         this.fireEvent("afterselectionchange", this);
8192     },
8193     // private
8194     handleDragableRowClick :  function(grid, rowIndex, e) 
8195     {
8196         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8197             this.selectRow(rowIndex, false);
8198             var view = this.grid.view ? this.grid.view : this.grid;
8199             view.focusRow(rowIndex);
8200              this.fireEvent("afterselectionchange", this);
8201         }
8202     },
8203     
8204     /**
8205      * Selects multiple rows.
8206      * @param {Array} rows Array of the indexes of the row to select
8207      * @param {Boolean} keepExisting (optional) True to keep existing selections
8208      */
8209     selectRows : function(rows, keepExisting){
8210         if(!keepExisting){
8211             this.clearSelections();
8212         }
8213         for(var i = 0, len = rows.length; i < len; i++){
8214             this.selectRow(rows[i], true);
8215         }
8216     },
8217
8218     /**
8219      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8220      * @param {Number} startRow The index of the first row in the range
8221      * @param {Number} endRow The index of the last row in the range
8222      * @param {Boolean} keepExisting (optional) True to retain existing selections
8223      */
8224     selectRange : function(startRow, endRow, keepExisting){
8225         if(this.locked) {
8226             return;
8227         }
8228         if(!keepExisting){
8229             this.clearSelections();
8230         }
8231         if(startRow <= endRow){
8232             for(var i = startRow; i <= endRow; i++){
8233                 this.selectRow(i, true);
8234             }
8235         }else{
8236             for(var i = startRow; i >= endRow; i--){
8237                 this.selectRow(i, true);
8238             }
8239         }
8240     },
8241
8242     /**
8243      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8244      * @param {Number} startRow The index of the first row in the range
8245      * @param {Number} endRow The index of the last row in the range
8246      */
8247     deselectRange : function(startRow, endRow, preventViewNotify){
8248         if(this.locked) {
8249             return;
8250         }
8251         for(var i = startRow; i <= endRow; i++){
8252             this.deselectRow(i, preventViewNotify);
8253         }
8254     },
8255
8256     /**
8257      * Selects a row.
8258      * @param {Number} row The index of the row to select
8259      * @param {Boolean} keepExisting (optional) True to keep existing selections
8260      */
8261     selectRow : function(index, keepExisting, preventViewNotify){
8262         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8263             return;
8264         }
8265         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8266             if(!keepExisting || this.singleSelect){
8267                 this.clearSelections();
8268             }
8269             var r = this.grid.ds.getAt(index);
8270             this.selections.add(r);
8271             this.last = this.lastActive = index;
8272             if(!preventViewNotify){
8273                 var view = this.grid.view ? this.grid.view : this.grid;
8274                 view.onRowSelect(index);
8275             }
8276             this.fireEvent("rowselect", this, index, r);
8277             this.fireEvent("selectionchange", this);
8278         }
8279     },
8280
8281     /**
8282      * Deselects a row.
8283      * @param {Number} row The index of the row to deselect
8284      */
8285     deselectRow : function(index, preventViewNotify){
8286         if(this.locked) {
8287             return;
8288         }
8289         if(this.last == index){
8290             this.last = false;
8291         }
8292         if(this.lastActive == index){
8293             this.lastActive = false;
8294         }
8295         var r = this.grid.ds.getAt(index);
8296         this.selections.remove(r);
8297         if(!preventViewNotify){
8298             var view = this.grid.view ? this.grid.view : this.grid;
8299             view.onRowDeselect(index);
8300         }
8301         this.fireEvent("rowdeselect", this, index);
8302         this.fireEvent("selectionchange", this);
8303     },
8304
8305     // private
8306     restoreLast : function(){
8307         if(this._last){
8308             this.last = this._last;
8309         }
8310     },
8311
8312     // private
8313     acceptsNav : function(row, col, cm){
8314         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8315     },
8316
8317     // private
8318     onEditorKey : function(field, e){
8319         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8320         if(k == e.TAB){
8321             e.stopEvent();
8322             ed.completeEdit();
8323             if(e.shiftKey){
8324                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8325             }else{
8326                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8327             }
8328         }else if(k == e.ENTER && !e.ctrlKey){
8329             e.stopEvent();
8330             ed.completeEdit();
8331             if(e.shiftKey){
8332                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8333             }else{
8334                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8335             }
8336         }else if(k == e.ESC){
8337             ed.cancelEdit();
8338         }
8339         if(newCell){
8340             g.startEditing(newCell[0], newCell[1]);
8341         }
8342     }
8343 });/*
8344  * Based on:
8345  * Ext JS Library 1.1.1
8346  * Copyright(c) 2006-2007, Ext JS, LLC.
8347  *
8348  * Originally Released Under LGPL - original licence link has changed is not relivant.
8349  *
8350  * Fork - LGPL
8351  * <script type="text/javascript">
8352  */
8353  
8354
8355 /**
8356  * @class Roo.grid.ColumnModel
8357  * @extends Roo.util.Observable
8358  * This is the default implementation of a ColumnModel used by the Grid. It defines
8359  * the columns in the grid.
8360  * <br>Usage:<br>
8361  <pre><code>
8362  var colModel = new Roo.grid.ColumnModel([
8363         {header: "Ticker", width: 60, sortable: true, locked: true},
8364         {header: "Company Name", width: 150, sortable: true},
8365         {header: "Market Cap.", width: 100, sortable: true},
8366         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8367         {header: "Employees", width: 100, sortable: true, resizable: false}
8368  ]);
8369  </code></pre>
8370  * <p>
8371  
8372  * The config options listed for this class are options which may appear in each
8373  * individual column definition.
8374  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8375  * @constructor
8376  * @param {Object} config An Array of column config objects. See this class's
8377  * config objects for details.
8378 */
8379 Roo.grid.ColumnModel = function(config){
8380         /**
8381      * The config passed into the constructor
8382      */
8383     this.config = []; //config;
8384     this.lookup = {};
8385
8386     // if no id, create one
8387     // if the column does not have a dataIndex mapping,
8388     // map it to the order it is in the config
8389     for(var i = 0, len = config.length; i < len; i++){
8390         this.addColumn(config[i]);
8391         
8392     }
8393
8394     /**
8395      * The width of columns which have no width specified (defaults to 100)
8396      * @type Number
8397      */
8398     this.defaultWidth = 100;
8399
8400     /**
8401      * Default sortable of columns which have no sortable specified (defaults to false)
8402      * @type Boolean
8403      */
8404     this.defaultSortable = false;
8405
8406     this.addEvents({
8407         /**
8408              * @event widthchange
8409              * Fires when the width of a column changes.
8410              * @param {ColumnModel} this
8411              * @param {Number} columnIndex The column index
8412              * @param {Number} newWidth The new width
8413              */
8414             "widthchange": true,
8415         /**
8416              * @event headerchange
8417              * Fires when the text of a header changes.
8418              * @param {ColumnModel} this
8419              * @param {Number} columnIndex The column index
8420              * @param {Number} newText The new header text
8421              */
8422             "headerchange": true,
8423         /**
8424              * @event hiddenchange
8425              * Fires when a column is hidden or "unhidden".
8426              * @param {ColumnModel} this
8427              * @param {Number} columnIndex The column index
8428              * @param {Boolean} hidden true if hidden, false otherwise
8429              */
8430             "hiddenchange": true,
8431             /**
8432          * @event columnmoved
8433          * Fires when a column is moved.
8434          * @param {ColumnModel} this
8435          * @param {Number} oldIndex
8436          * @param {Number} newIndex
8437          */
8438         "columnmoved" : true,
8439         /**
8440          * @event columlockchange
8441          * Fires when a column's locked state is changed
8442          * @param {ColumnModel} this
8443          * @param {Number} colIndex
8444          * @param {Boolean} locked true if locked
8445          */
8446         "columnlockchange" : true
8447     });
8448     Roo.grid.ColumnModel.superclass.constructor.call(this);
8449 };
8450 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8451     /**
8452      * @cfg {String} header The header text to display in the Grid view.
8453      */
8454         /**
8455      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8456      */
8457         /**
8458      * @cfg {String} smHeader Header at Bootsrap Small width
8459      */
8460         /**
8461      * @cfg {String} mdHeader Header at Bootsrap Medium width
8462      */
8463         /**
8464      * @cfg {String} lgHeader Header at Bootsrap Large width
8465      */
8466         /**
8467      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8468      */
8469     /**
8470      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8471      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8472      * specified, the column's index is used as an index into the Record's data Array.
8473      */
8474     /**
8475      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8476      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8477      */
8478     /**
8479      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8480      * Defaults to the value of the {@link #defaultSortable} property.
8481      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8482      */
8483     /**
8484      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8485      */
8486     /**
8487      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8488      */
8489     /**
8490      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8491      */
8492     /**
8493      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8494      */
8495     /**
8496      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8497      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8498      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8499      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8500      */
8501        /**
8502      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8503      */
8504     /**
8505      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8506      */
8507     /**
8508      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8509      */
8510     /**
8511      * @cfg {String} cursor (Optional)
8512      */
8513     /**
8514      * @cfg {String} tooltip (Optional)
8515      */
8516     /**
8517      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8518      */
8519     /**
8520      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8521      */
8522     /**
8523      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8524      */
8525     /**
8526      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8527      */
8528         /**
8529      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8530      */
8531     /**
8532      * Returns the id of the column at the specified index.
8533      * @param {Number} index The column index
8534      * @return {String} the id
8535      */
8536     getColumnId : function(index){
8537         return this.config[index].id;
8538     },
8539
8540     /**
8541      * Returns the column for a specified id.
8542      * @param {String} id The column id
8543      * @return {Object} the column
8544      */
8545     getColumnById : function(id){
8546         return this.lookup[id];
8547     },
8548
8549     
8550     /**
8551      * Returns the column Object for a specified dataIndex.
8552      * @param {String} dataIndex The column dataIndex
8553      * @return {Object|Boolean} the column or false if not found
8554      */
8555     getColumnByDataIndex: function(dataIndex){
8556         var index = this.findColumnIndex(dataIndex);
8557         return index > -1 ? this.config[index] : false;
8558     },
8559     
8560     /**
8561      * Returns the index for a specified column id.
8562      * @param {String} id The column id
8563      * @return {Number} the index, or -1 if not found
8564      */
8565     getIndexById : function(id){
8566         for(var i = 0, len = this.config.length; i < len; i++){
8567             if(this.config[i].id == id){
8568                 return i;
8569             }
8570         }
8571         return -1;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column dataIndex.
8576      * @param {String} dataIndex The column dataIndex
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     
8580     findColumnIndex : function(dataIndex){
8581         for(var i = 0, len = this.config.length; i < len; i++){
8582             if(this.config[i].dataIndex == dataIndex){
8583                 return i;
8584             }
8585         }
8586         return -1;
8587     },
8588     
8589     
8590     moveColumn : function(oldIndex, newIndex){
8591         var c = this.config[oldIndex];
8592         this.config.splice(oldIndex, 1);
8593         this.config.splice(newIndex, 0, c);
8594         this.dataMap = null;
8595         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8596     },
8597
8598     isLocked : function(colIndex){
8599         return this.config[colIndex].locked === true;
8600     },
8601
8602     setLocked : function(colIndex, value, suppressEvent){
8603         if(this.isLocked(colIndex) == value){
8604             return;
8605         }
8606         this.config[colIndex].locked = value;
8607         if(!suppressEvent){
8608             this.fireEvent("columnlockchange", this, colIndex, value);
8609         }
8610     },
8611
8612     getTotalLockedWidth : function(){
8613         var totalWidth = 0;
8614         for(var i = 0; i < this.config.length; i++){
8615             if(this.isLocked(i) && !this.isHidden(i)){
8616                 this.totalWidth += this.getColumnWidth(i);
8617             }
8618         }
8619         return totalWidth;
8620     },
8621
8622     getLockedCount : function(){
8623         for(var i = 0, len = this.config.length; i < len; i++){
8624             if(!this.isLocked(i)){
8625                 return i;
8626             }
8627         }
8628         
8629         return this.config.length;
8630     },
8631
8632     /**
8633      * Returns the number of columns.
8634      * @return {Number}
8635      */
8636     getColumnCount : function(visibleOnly){
8637         if(visibleOnly === true){
8638             var c = 0;
8639             for(var i = 0, len = this.config.length; i < len; i++){
8640                 if(!this.isHidden(i)){
8641                     c++;
8642                 }
8643             }
8644             return c;
8645         }
8646         return this.config.length;
8647     },
8648
8649     /**
8650      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8651      * @param {Function} fn
8652      * @param {Object} scope (optional)
8653      * @return {Array} result
8654      */
8655     getColumnsBy : function(fn, scope){
8656         var r = [];
8657         for(var i = 0, len = this.config.length; i < len; i++){
8658             var c = this.config[i];
8659             if(fn.call(scope||this, c, i) === true){
8660                 r[r.length] = c;
8661             }
8662         }
8663         return r;
8664     },
8665
8666     /**
8667      * Returns true if the specified column is sortable.
8668      * @param {Number} col The column index
8669      * @return {Boolean}
8670      */
8671     isSortable : function(col){
8672         if(typeof this.config[col].sortable == "undefined"){
8673             return this.defaultSortable;
8674         }
8675         return this.config[col].sortable;
8676     },
8677
8678     /**
8679      * Returns the rendering (formatting) function defined for the column.
8680      * @param {Number} col The column index.
8681      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8682      */
8683     getRenderer : function(col){
8684         if(!this.config[col].renderer){
8685             return Roo.grid.ColumnModel.defaultRenderer;
8686         }
8687         return this.config[col].renderer;
8688     },
8689
8690     /**
8691      * Sets the rendering (formatting) function for a column.
8692      * @param {Number} col The column index
8693      * @param {Function} fn The function to use to process the cell's raw data
8694      * to return HTML markup for the grid view. The render function is called with
8695      * the following parameters:<ul>
8696      * <li>Data value.</li>
8697      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8698      * <li>css A CSS style string to apply to the table cell.</li>
8699      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8700      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8701      * <li>Row index</li>
8702      * <li>Column index</li>
8703      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8704      */
8705     setRenderer : function(col, fn){
8706         this.config[col].renderer = fn;
8707     },
8708
8709     /**
8710      * Returns the width for the specified column.
8711      * @param {Number} col The column index
8712      * @param (optional) {String} gridSize bootstrap width size.
8713      * @return {Number}
8714      */
8715     getColumnWidth : function(col, gridSize)
8716         {
8717                 var cfg = this.config[col];
8718                 
8719                 if (typeof(gridSize) == 'undefined') {
8720                         return cfg.width * 1 || this.defaultWidth;
8721                 }
8722                 if (gridSize === false) { // if we set it..
8723                         return cfg.width || false;
8724                 }
8725                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8726                 
8727                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8728                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8729                                 continue;
8730                         }
8731                         return cfg[ sizes[i] ];
8732                 }
8733                 return 1;
8734                 
8735     },
8736
8737     /**
8738      * Sets the width for a column.
8739      * @param {Number} col The column index
8740      * @param {Number} width The new width
8741      */
8742     setColumnWidth : function(col, width, suppressEvent){
8743         this.config[col].width = width;
8744         this.totalWidth = null;
8745         if(!suppressEvent){
8746              this.fireEvent("widthchange", this, col, width);
8747         }
8748     },
8749
8750     /**
8751      * Returns the total width of all columns.
8752      * @param {Boolean} includeHidden True to include hidden column widths
8753      * @return {Number}
8754      */
8755     getTotalWidth : function(includeHidden){
8756         if(!this.totalWidth){
8757             this.totalWidth = 0;
8758             for(var i = 0, len = this.config.length; i < len; i++){
8759                 if(includeHidden || !this.isHidden(i)){
8760                     this.totalWidth += this.getColumnWidth(i);
8761                 }
8762             }
8763         }
8764         return this.totalWidth;
8765     },
8766
8767     /**
8768      * Returns the header for the specified column.
8769      * @param {Number} col The column index
8770      * @return {String}
8771      */
8772     getColumnHeader : function(col){
8773         return this.config[col].header;
8774     },
8775
8776     /**
8777      * Sets the header for a column.
8778      * @param {Number} col The column index
8779      * @param {String} header The new header
8780      */
8781     setColumnHeader : function(col, header){
8782         this.config[col].header = header;
8783         this.fireEvent("headerchange", this, col, header);
8784     },
8785
8786     /**
8787      * Returns the tooltip for the specified column.
8788      * @param {Number} col The column index
8789      * @return {String}
8790      */
8791     getColumnTooltip : function(col){
8792             return this.config[col].tooltip;
8793     },
8794     /**
8795      * Sets the tooltip for a column.
8796      * @param {Number} col The column index
8797      * @param {String} tooltip The new tooltip
8798      */
8799     setColumnTooltip : function(col, tooltip){
8800             this.config[col].tooltip = tooltip;
8801     },
8802
8803     /**
8804      * Returns the dataIndex for the specified column.
8805      * @param {Number} col The column index
8806      * @return {Number}
8807      */
8808     getDataIndex : function(col){
8809         return this.config[col].dataIndex;
8810     },
8811
8812     /**
8813      * Sets the dataIndex for a column.
8814      * @param {Number} col The column index
8815      * @param {Number} dataIndex The new dataIndex
8816      */
8817     setDataIndex : function(col, dataIndex){
8818         this.config[col].dataIndex = dataIndex;
8819     },
8820
8821     
8822     
8823     /**
8824      * Returns true if the cell is editable.
8825      * @param {Number} colIndex The column index
8826      * @param {Number} rowIndex The row index - this is nto actually used..?
8827      * @return {Boolean}
8828      */
8829     isCellEditable : function(colIndex, rowIndex){
8830         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8831     },
8832
8833     /**
8834      * Returns the editor defined for the cell/column.
8835      * return false or null to disable editing.
8836      * @param {Number} colIndex The column index
8837      * @param {Number} rowIndex The row index
8838      * @return {Object}
8839      */
8840     getCellEditor : function(colIndex, rowIndex){
8841         return this.config[colIndex].editor;
8842     },
8843
8844     /**
8845      * Sets if a column is editable.
8846      * @param {Number} col The column index
8847      * @param {Boolean} editable True if the column is editable
8848      */
8849     setEditable : function(col, editable){
8850         this.config[col].editable = editable;
8851     },
8852
8853
8854     /**
8855      * Returns true if the column is hidden.
8856      * @param {Number} colIndex The column index
8857      * @return {Boolean}
8858      */
8859     isHidden : function(colIndex){
8860         return this.config[colIndex].hidden;
8861     },
8862
8863
8864     /**
8865      * Returns true if the column width cannot be changed
8866      */
8867     isFixed : function(colIndex){
8868         return this.config[colIndex].fixed;
8869     },
8870
8871     /**
8872      * Returns true if the column can be resized
8873      * @return {Boolean}
8874      */
8875     isResizable : function(colIndex){
8876         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8877     },
8878     /**
8879      * Sets if a column is hidden.
8880      * @param {Number} colIndex The column index
8881      * @param {Boolean} hidden True if the column is hidden
8882      */
8883     setHidden : function(colIndex, hidden){
8884         this.config[colIndex].hidden = hidden;
8885         this.totalWidth = null;
8886         this.fireEvent("hiddenchange", this, colIndex, hidden);
8887     },
8888
8889     /**
8890      * Sets the editor for a column.
8891      * @param {Number} col The column index
8892      * @param {Object} editor The editor object
8893      */
8894     setEditor : function(col, editor){
8895         this.config[col].editor = editor;
8896     },
8897     /**
8898      * Add a column (experimental...) - defaults to adding to the end..
8899      * @param {Object} config 
8900     */
8901     addColumn : function(c)
8902     {
8903     
8904         var i = this.config.length;
8905         this.config[i] = c;
8906         
8907         if(typeof c.dataIndex == "undefined"){
8908             c.dataIndex = i;
8909         }
8910         if(typeof c.renderer == "string"){
8911             c.renderer = Roo.util.Format[c.renderer];
8912         }
8913         if(typeof c.id == "undefined"){
8914             c.id = Roo.id();
8915         }
8916         if(c.editor && c.editor.xtype){
8917             c.editor  = Roo.factory(c.editor, Roo.grid);
8918         }
8919         if(c.editor && c.editor.isFormField){
8920             c.editor = new Roo.grid.GridEditor(c.editor);
8921         }
8922         this.lookup[c.id] = c;
8923     }
8924     
8925 });
8926
8927 Roo.grid.ColumnModel.defaultRenderer = function(value)
8928 {
8929     if(typeof value == "object") {
8930         return value;
8931     }
8932         if(typeof value == "string" && value.length < 1){
8933             return "&#160;";
8934         }
8935     
8936         return String.format("{0}", value);
8937 };
8938
8939 // Alias for backwards compatibility
8940 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8941 /*
8942  * Based on:
8943  * Ext JS Library 1.1.1
8944  * Copyright(c) 2006-2007, Ext JS, LLC.
8945  *
8946  * Originally Released Under LGPL - original licence link has changed is not relivant.
8947  *
8948  * Fork - LGPL
8949  * <script type="text/javascript">
8950  */
8951  
8952 /**
8953  * @class Roo.LoadMask
8954  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8955  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8956  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8957  * element's UpdateManager load indicator and will be destroyed after the initial load.
8958  * @constructor
8959  * Create a new LoadMask
8960  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8961  * @param {Object} config The config object
8962  */
8963 Roo.LoadMask = function(el, config){
8964     this.el = Roo.get(el);
8965     Roo.apply(this, config);
8966     if(this.store){
8967         this.store.on('beforeload', this.onBeforeLoad, this);
8968         this.store.on('load', this.onLoad, this);
8969         this.store.on('loadexception', this.onLoadException, this);
8970         this.removeMask = false;
8971     }else{
8972         var um = this.el.getUpdateManager();
8973         um.showLoadIndicator = false; // disable the default indicator
8974         um.on('beforeupdate', this.onBeforeLoad, this);
8975         um.on('update', this.onLoad, this);
8976         um.on('failure', this.onLoad, this);
8977         this.removeMask = true;
8978     }
8979 };
8980
8981 Roo.LoadMask.prototype = {
8982     /**
8983      * @cfg {Boolean} removeMask
8984      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8985      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8986      */
8987     removeMask : false,
8988     /**
8989      * @cfg {String} msg
8990      * The text to display in a centered loading message box (defaults to 'Loading...')
8991      */
8992     msg : 'Loading...',
8993     /**
8994      * @cfg {String} msgCls
8995      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8996      */
8997     msgCls : 'x-mask-loading',
8998
8999     /**
9000      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9001      * @type Boolean
9002      */
9003     disabled: false,
9004
9005     /**
9006      * Disables the mask to prevent it from being displayed
9007      */
9008     disable : function(){
9009        this.disabled = true;
9010     },
9011
9012     /**
9013      * Enables the mask so that it can be displayed
9014      */
9015     enable : function(){
9016         this.disabled = false;
9017     },
9018     
9019     onLoadException : function()
9020     {
9021         Roo.log(arguments);
9022         
9023         if (typeof(arguments[3]) != 'undefined') {
9024             Roo.MessageBox.alert("Error loading",arguments[3]);
9025         } 
9026         /*
9027         try {
9028             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9029                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9030             }   
9031         } catch(e) {
9032             
9033         }
9034         */
9035     
9036         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9037     },
9038     // private
9039     onLoad : function()
9040     {
9041         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9042     },
9043
9044     // private
9045     onBeforeLoad : function(){
9046         if(!this.disabled){
9047             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9048         }
9049     },
9050
9051     // private
9052     destroy : function(){
9053         if(this.store){
9054             this.store.un('beforeload', this.onBeforeLoad, this);
9055             this.store.un('load', this.onLoad, this);
9056             this.store.un('loadexception', this.onLoadException, this);
9057         }else{
9058             var um = this.el.getUpdateManager();
9059             um.un('beforeupdate', this.onBeforeLoad, this);
9060             um.un('update', this.onLoad, this);
9061             um.un('failure', this.onLoad, this);
9062         }
9063     }
9064 };/**
9065  * @class Roo.bootstrap.Table
9066  * @licence LGBL
9067  * @extends Roo.bootstrap.Component
9068  * @children Roo.bootstrap.TableBody
9069  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9070  * Similar to Roo.grid.Grid
9071  * <pre><code>
9072  var table = Roo.factory({
9073     xtype : 'Table',
9074     xns : Roo.bootstrap,
9075     autoSizeColumns: true,
9076     
9077     
9078     store : {
9079         xtype : 'Store',
9080         xns : Roo.data,
9081         remoteSort : true,
9082         sortInfo : { direction : 'ASC', field: 'name' },
9083         proxy : {
9084            xtype : 'HttpProxy',
9085            xns : Roo.data,
9086            method : 'GET',
9087            url : 'https://example.com/some.data.url.json'
9088         },
9089         reader : {
9090            xtype : 'JsonReader',
9091            xns : Roo.data,
9092            fields : [ 'id', 'name', whatever' ],
9093            id : 'id',
9094            root : 'data'
9095         }
9096     },
9097     cm : [
9098         {
9099             xtype : 'ColumnModel',
9100             xns : Roo.grid,
9101             align : 'center',
9102             cursor : 'pointer',
9103             dataIndex : 'is_in_group',
9104             header : "Name",
9105             sortable : true,
9106             renderer : function(v, x , r) {  
9107             
9108                 return String.format("{0}", v)
9109             }
9110             width : 3
9111         } // more columns..
9112     ],
9113     selModel : {
9114         xtype : 'RowSelectionModel',
9115         xns : Roo.bootstrap.Table
9116         // you can add listeners to catch selection change here....
9117     }
9118      
9119
9120  });
9121  // set any options
9122  grid.render(Roo.get("some-div"));
9123 </code></pre>
9124
9125 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9126
9127
9128
9129  *
9130  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9131  * @cfg {Roo.data.Store} store The data store to use
9132  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9133  * 
9134  * @cfg {String} cls table class
9135  *
9136  * 
9137  * @cfg {boolean} striped Should the rows be alternative striped
9138  * @cfg {boolean} bordered Add borders to the table
9139  * @cfg {boolean} hover Add hover highlighting
9140  * @cfg {boolean} condensed Format condensed
9141  * @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,
9142  *                also adds table-responsive (see bootstrap docs for details)
9143  * @cfg {Boolean} loadMask (true|false) default false
9144  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9145  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9146  * @cfg {Boolean} rowSelection (true|false) default false
9147  * @cfg {Boolean} cellSelection (true|false) default false
9148  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9149  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9150  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9151  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9152  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9153  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9154  * 
9155  * @constructor
9156  * Create a new Table
9157  * @param {Object} config The config object
9158  */
9159
9160 Roo.bootstrap.Table = function(config)
9161 {
9162     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9163      
9164     // BC...
9165     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9166     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9167     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9168     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9169     
9170     this.view = this; // compat with grid.
9171     
9172     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9173     if (this.sm) {
9174         this.sm.grid = this;
9175         this.selModel = Roo.factory(this.sm, Roo.grid);
9176         this.sm = this.selModel;
9177         this.sm.xmodule = this.xmodule || false;
9178     }
9179     
9180     if (this.cm && typeof(this.cm.config) == 'undefined') {
9181         this.colModel = new Roo.grid.ColumnModel(this.cm);
9182         this.cm = this.colModel;
9183         this.cm.xmodule = this.xmodule || false;
9184     }
9185     if (this.store) {
9186         this.store= Roo.factory(this.store, Roo.data);
9187         this.ds = this.store;
9188         this.ds.xmodule = this.xmodule || false;
9189          
9190     }
9191     if (this.footer && this.store) {
9192         this.footer.dataSource = this.ds;
9193         this.footer = Roo.factory(this.footer);
9194     }
9195     
9196     /** @private */
9197     this.addEvents({
9198         /**
9199          * @event cellclick
9200          * Fires when a cell is clicked
9201          * @param {Roo.bootstrap.Table} this
9202          * @param {Roo.Element} el
9203          * @param {Number} rowIndex
9204          * @param {Number} columnIndex
9205          * @param {Roo.EventObject} e
9206          */
9207         "cellclick" : true,
9208         /**
9209          * @event celldblclick
9210          * Fires when a cell is double clicked
9211          * @param {Roo.bootstrap.Table} this
9212          * @param {Roo.Element} el
9213          * @param {Number} rowIndex
9214          * @param {Number} columnIndex
9215          * @param {Roo.EventObject} e
9216          */
9217         "celldblclick" : true,
9218         /**
9219          * @event rowclick
9220          * Fires when a row is clicked
9221          * @param {Roo.bootstrap.Table} this
9222          * @param {Roo.Element} el
9223          * @param {Number} rowIndex
9224          * @param {Roo.EventObject} e
9225          */
9226         "rowclick" : true,
9227         /**
9228          * @event rowdblclick
9229          * Fires when a row is double clicked
9230          * @param {Roo.bootstrap.Table} this
9231          * @param {Roo.Element} el
9232          * @param {Number} rowIndex
9233          * @param {Roo.EventObject} e
9234          */
9235         "rowdblclick" : true,
9236         /**
9237          * @event mouseover
9238          * Fires when a mouseover occur
9239          * @param {Roo.bootstrap.Table} this
9240          * @param {Roo.Element} el
9241          * @param {Number} rowIndex
9242          * @param {Number} columnIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "mouseover" : true,
9246         /**
9247          * @event mouseout
9248          * Fires when a mouseout occur
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Number} columnIndex
9253          * @param {Roo.EventObject} e
9254          */
9255         "mouseout" : true,
9256         /**
9257          * @event rowclass
9258          * Fires when a row is rendered, so you can change add a style to it.
9259          * @param {Roo.bootstrap.Table} this
9260          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9261          */
9262         'rowclass' : true,
9263           /**
9264          * @event rowsrendered
9265          * Fires when all the  rows have been rendered
9266          * @param {Roo.bootstrap.Table} this
9267          */
9268         'rowsrendered' : true,
9269         /**
9270          * @event contextmenu
9271          * The raw contextmenu event for the entire grid.
9272          * @param {Roo.EventObject} e
9273          */
9274         "contextmenu" : true,
9275         /**
9276          * @event rowcontextmenu
9277          * Fires when a row is right clicked
9278          * @param {Roo.bootstrap.Table} this
9279          * @param {Number} rowIndex
9280          * @param {Roo.EventObject} e
9281          */
9282         "rowcontextmenu" : true,
9283         /**
9284          * @event cellcontextmenu
9285          * Fires when a cell is right clicked
9286          * @param {Roo.bootstrap.Table} this
9287          * @param {Number} rowIndex
9288          * @param {Number} cellIndex
9289          * @param {Roo.EventObject} e
9290          */
9291          "cellcontextmenu" : true,
9292          /**
9293          * @event headercontextmenu
9294          * Fires when a header is right clicked
9295          * @param {Roo.bootstrap.Table} this
9296          * @param {Number} columnIndex
9297          * @param {Roo.EventObject} e
9298          */
9299         "headercontextmenu" : true,
9300         /**
9301          * @event mousedown
9302          * The raw mousedown event for the entire grid.
9303          * @param {Roo.EventObject} e
9304          */
9305         "mousedown" : true
9306         
9307     });
9308 };
9309
9310 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9311     
9312     cls: false,
9313     
9314     striped : false,
9315     scrollBody : false,
9316     bordered: false,
9317     hover:  false,
9318     condensed : false,
9319     responsive : false,
9320     sm : false,
9321     cm : false,
9322     store : false,
9323     loadMask : false,
9324     footerShow : true,
9325     headerShow : true,
9326     enableColumnResize: true,
9327   
9328     rowSelection : false,
9329     cellSelection : false,
9330     layout : false,
9331
9332     minColumnWidth : 50,
9333     
9334     // Roo.Element - the tbody
9335     bodyEl: false,  // <tbody> Roo.Element - thead element    
9336     headEl: false,  // <thead> Roo.Element - thead element
9337     resizeProxy : false, // proxy element for dragging?
9338
9339
9340     
9341     container: false, // used by gridpanel...
9342     
9343     lazyLoad : false,
9344     
9345     CSS : Roo.util.CSS,
9346     
9347     auto_hide_footer : false,
9348     
9349     view: false, // actually points to this..
9350     
9351     getAutoCreate : function()
9352     {
9353         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9354         
9355         cfg = {
9356             tag: 'table',
9357             cls : 'table', 
9358             cn : []
9359         };
9360         // this get's auto added by panel.Grid
9361         if (this.scrollBody) {
9362             cfg.cls += ' table-body-fixed';
9363         }    
9364         if (this.striped) {
9365             cfg.cls += ' table-striped';
9366         }
9367         
9368         if (this.hover) {
9369             cfg.cls += ' table-hover';
9370         }
9371         if (this.bordered) {
9372             cfg.cls += ' table-bordered';
9373         }
9374         if (this.condensed) {
9375             cfg.cls += ' table-condensed';
9376         }
9377         
9378         if (this.responsive) {
9379             cfg.cls += ' table-responsive';
9380         }
9381         
9382         if (this.cls) {
9383             cfg.cls+=  ' ' +this.cls;
9384         }
9385         
9386         
9387         
9388         if (this.layout) {
9389             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9390         }
9391         
9392         if(this.store || this.cm){
9393             if(this.headerShow){
9394                 cfg.cn.push(this.renderHeader());
9395             }
9396             
9397             cfg.cn.push(this.renderBody());
9398             
9399             if(this.footerShow){
9400                 cfg.cn.push(this.renderFooter());
9401             }
9402             // where does this come from?
9403             //cfg.cls+=  ' TableGrid';
9404         }
9405         
9406         return { cn : [ cfg ] };
9407     },
9408     
9409     initEvents : function()
9410     {   
9411         if(!this.store || !this.cm){
9412             return;
9413         }
9414         if (this.selModel) {
9415             this.selModel.initEvents();
9416         }
9417         
9418         
9419         //Roo.log('initEvents with ds!!!!');
9420         
9421         this.bodyEl = this.el.select('tbody', true).first();
9422         this.headEl = this.el.select('thead', true).first();
9423         this.mainFoot = this.el.select('tfoot', true).first();
9424         
9425         
9426         
9427         
9428         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9429             e.on('click', this.sort, this);
9430         }, this);
9431         
9432         
9433         // why is this done????? = it breaks dialogs??
9434         //this.parent().el.setStyle('position', 'relative');
9435         
9436         
9437         if (this.footer) {
9438             this.footer.parentId = this.id;
9439             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9440             
9441             if(this.lazyLoad){
9442                 this.el.select('tfoot tr td').first().addClass('hide');
9443             }
9444         } 
9445         
9446         if(this.loadMask) {
9447             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9448         }
9449         
9450         this.store.on('load', this.onLoad, this);
9451         this.store.on('beforeload', this.onBeforeLoad, this);
9452         this.store.on('update', this.onUpdate, this);
9453         this.store.on('add', this.onAdd, this);
9454         this.store.on("clear", this.clear, this);
9455         
9456         this.el.on("contextmenu", this.onContextMenu, this);
9457         
9458         
9459         this.cm.on("headerchange", this.onHeaderChange, this);
9460         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9461
9462  //?? does bodyEl get replaced on render?
9463         this.bodyEl.on("click", this.onClick, this);
9464         this.bodyEl.on("dblclick", this.onDblClick, this);        
9465         this.bodyEl.on('scroll', this.onBodyScroll, this);
9466
9467         // guessing mainbody will work - this relays usually caught by selmodel at present.
9468         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9469   
9470   
9471         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9472         
9473   
9474         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9475             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9476         }
9477         
9478         this.initCSS();
9479     },
9480     // Compatibility with grid - we implement all the view features at present.
9481     getView : function()
9482     {
9483         return this;
9484     },
9485     
9486     initCSS : function()
9487     {
9488         
9489         
9490         var cm = this.cm, styles = [];
9491         this.CSS.removeStyleSheet(this.id + '-cssrules');
9492         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9493         // we can honour xs/sm/md/xl  as widths...
9494         // we first have to decide what widht we are currently at...
9495         var sz = Roo.getGridSize();
9496         
9497         var total = 0;
9498         var last = -1;
9499         var cols = []; // visable cols.
9500         var total_abs = 0;
9501         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9502             var w = cm.getColumnWidth(i, false);
9503             if(cm.isHidden(i)){
9504                 cols.push( { rel : false, abs : 0 });
9505                 continue;
9506             }
9507             if (w !== false) {
9508                 cols.push( { rel : false, abs : w });
9509                 total_abs += w;
9510                 last = i; // not really..
9511                 continue;
9512             }
9513             var w = cm.getColumnWidth(i, sz);
9514             if (w > 0) {
9515                 last = i
9516             }
9517             total += w;
9518             cols.push( { rel : w, abs : false });
9519         }
9520         
9521         var avail = this.bodyEl.dom.clientWidth - total_abs;
9522         
9523         var unitWidth = Math.floor(avail / total);
9524         var rem = avail - (unitWidth * total);
9525         
9526         var hidden, width, pos = 0 , splithide , left;
9527         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9528             
9529             hidden = 'display:none;';
9530             left = '';
9531             width  = 'width:0px;';
9532             splithide = '';
9533             if(!cm.isHidden(i)){
9534                 hidden = '';
9535                 
9536                 
9537                 // we can honour xs/sm/md/xl ?
9538                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9539                 if (w===0) {
9540                     hidden = 'display:none;';
9541                 }
9542                 // width should return a small number...
9543                 if (i == last) {
9544                     w+=rem; // add the remaining with..
9545                 }
9546                 pos += w;
9547                 left = "left:" + (pos -4) + "px;";
9548                 width = "width:" + w+ "px;";
9549                 
9550             }
9551             if (this.responsive) {
9552                 width = '';
9553                 left = '';
9554                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9555                 splithide = 'display: none;';
9556             }
9557             
9558             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9559             if (this.headEl) {
9560                 if (i == last) {
9561                     splithide = 'display:none;';
9562                 }
9563                 
9564                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9565                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9566                 );
9567             }
9568             
9569         }
9570         //Roo.log(styles.join(''));
9571         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9572         
9573     },
9574     
9575     
9576     
9577     onContextMenu : function(e, t)
9578     {
9579         this.processEvent("contextmenu", e);
9580     },
9581     
9582     processEvent : function(name, e)
9583     {
9584         if (name != 'touchstart' ) {
9585             this.fireEvent(name, e);    
9586         }
9587         
9588         var t = e.getTarget();
9589         
9590         var cell = Roo.get(t);
9591         
9592         if(!cell){
9593             return;
9594         }
9595         
9596         if(cell.findParent('tfoot', false, true)){
9597             return;
9598         }
9599         
9600         if(cell.findParent('thead', false, true)){
9601             
9602             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9603                 cell = Roo.get(t).findParent('th', false, true);
9604                 if (!cell) {
9605                     Roo.log("failed to find th in thead?");
9606                     Roo.log(e.getTarget());
9607                     return;
9608                 }
9609             }
9610             
9611             var cellIndex = cell.dom.cellIndex;
9612             
9613             var ename = name == 'touchstart' ? 'click' : name;
9614             this.fireEvent("header" + ename, this, cellIndex, e);
9615             
9616             return;
9617         }
9618         
9619         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9620             cell = Roo.get(t).findParent('td', false, true);
9621             if (!cell) {
9622                 Roo.log("failed to find th in tbody?");
9623                 Roo.log(e.getTarget());
9624                 return;
9625             }
9626         }
9627         
9628         var row = cell.findParent('tr', false, true);
9629         var cellIndex = cell.dom.cellIndex;
9630         var rowIndex = row.dom.rowIndex - 1;
9631         
9632         if(row !== false){
9633             
9634             this.fireEvent("row" + name, this, rowIndex, e);
9635             
9636             if(cell !== false){
9637             
9638                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9639             }
9640         }
9641         
9642     },
9643     
9644     onMouseover : function(e, el)
9645     {
9646         var cell = Roo.get(el);
9647         
9648         if(!cell){
9649             return;
9650         }
9651         
9652         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9653             cell = cell.findParent('td', false, true);
9654         }
9655         
9656         var row = cell.findParent('tr', false, true);
9657         var cellIndex = cell.dom.cellIndex;
9658         var rowIndex = row.dom.rowIndex - 1; // start from 0
9659         
9660         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9661         
9662     },
9663     
9664     onMouseout : function(e, el)
9665     {
9666         var cell = Roo.get(el);
9667         
9668         if(!cell){
9669             return;
9670         }
9671         
9672         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9673             cell = cell.findParent('td', false, true);
9674         }
9675         
9676         var row = cell.findParent('tr', false, true);
9677         var cellIndex = cell.dom.cellIndex;
9678         var rowIndex = row.dom.rowIndex - 1; // start from 0
9679         
9680         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9681         
9682     },
9683     
9684     onClick : function(e, el)
9685     {
9686         var cell = Roo.get(el);
9687         
9688         if(!cell || (!this.cellSelection && !this.rowSelection)){
9689             return;
9690         }
9691         
9692         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9693             cell = cell.findParent('td', false, true);
9694         }
9695         
9696         if(!cell || typeof(cell) == 'undefined'){
9697             return;
9698         }
9699         
9700         var row = cell.findParent('tr', false, true);
9701         
9702         if(!row || typeof(row) == 'undefined'){
9703             return;
9704         }
9705         
9706         var cellIndex = cell.dom.cellIndex;
9707         var rowIndex = this.getRowIndex(row);
9708         
9709         // why??? - should these not be based on SelectionModel?
9710         //if(this.cellSelection){
9711             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9712         //}
9713         
9714         //if(this.rowSelection){
9715             this.fireEvent('rowclick', this, row, rowIndex, e);
9716         //}
9717          
9718     },
9719         
9720     onDblClick : function(e,el)
9721     {
9722         var cell = Roo.get(el);
9723         
9724         if(!cell || (!this.cellSelection && !this.rowSelection)){
9725             return;
9726         }
9727         
9728         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9729             cell = cell.findParent('td', false, true);
9730         }
9731         
9732         if(!cell || typeof(cell) == 'undefined'){
9733             return;
9734         }
9735         
9736         var row = cell.findParent('tr', false, true);
9737         
9738         if(!row || typeof(row) == 'undefined'){
9739             return;
9740         }
9741         
9742         var cellIndex = cell.dom.cellIndex;
9743         var rowIndex = this.getRowIndex(row);
9744         
9745         if(this.cellSelection){
9746             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9747         }
9748         
9749         if(this.rowSelection){
9750             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9751         }
9752     },
9753     findRowIndex : function(el)
9754     {
9755         var cell = Roo.get(el);
9756         if(!cell) {
9757             return false;
9758         }
9759         var row = cell.findParent('tr', false, true);
9760         
9761         if(!row || typeof(row) == 'undefined'){
9762             return false;
9763         }
9764         return this.getRowIndex(row);
9765     },
9766     sort : function(e,el)
9767     {
9768         var col = Roo.get(el);
9769         
9770         if(!col.hasClass('sortable')){
9771             return;
9772         }
9773         
9774         var sort = col.attr('sort');
9775         var dir = 'ASC';
9776         
9777         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9778             dir = 'DESC';
9779         }
9780         
9781         this.store.sortInfo = {field : sort, direction : dir};
9782         
9783         if (this.footer) {
9784             Roo.log("calling footer first");
9785             this.footer.onClick('first');
9786         } else {
9787         
9788             this.store.load({ params : { start : 0 } });
9789         }
9790     },
9791     
9792     renderHeader : function()
9793     {
9794         var header = {
9795             tag: 'thead',
9796             cn : []
9797         };
9798         
9799         var cm = this.cm;
9800         this.totalWidth = 0;
9801         
9802         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9803             
9804             var config = cm.config[i];
9805             
9806             var c = {
9807                 tag: 'th',
9808                 cls : 'x-hcol-' + i,
9809                 style : '',
9810                 
9811                 html: cm.getColumnHeader(i)
9812             };
9813             
9814             var tooltip = cm.getColumnTooltip(i);
9815             if (tooltip) {
9816                 c.tooltip = tooltip;
9817             }
9818             
9819             
9820             var hh = '';
9821             
9822             if(typeof(config.sortable) != 'undefined' && config.sortable){
9823                 c.cls += ' sortable';
9824                 c.html = '<i class="fa"></i>' + c.html;
9825             }
9826             
9827             // could use BS4 hidden-..-down 
9828             
9829             if(typeof(config.lgHeader) != 'undefined'){
9830                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9831             }
9832             
9833             if(typeof(config.mdHeader) != 'undefined'){
9834                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9835             }
9836             
9837             if(typeof(config.smHeader) != 'undefined'){
9838                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9839             }
9840             
9841             if(typeof(config.xsHeader) != 'undefined'){
9842                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9843             }
9844             
9845             if(hh.length){
9846                 c.html = hh;
9847             }
9848             
9849             if(typeof(config.tooltip) != 'undefined'){
9850                 c.tooltip = config.tooltip;
9851             }
9852             
9853             if(typeof(config.colspan) != 'undefined'){
9854                 c.colspan = config.colspan;
9855             }
9856             
9857             // hidden is handled by CSS now
9858             
9859             if(typeof(config.dataIndex) != 'undefined'){
9860                 c.sort = config.dataIndex;
9861             }
9862             
9863            
9864             
9865             if(typeof(config.align) != 'undefined' && config.align.length){
9866                 c.style += ' text-align:' + config.align + ';';
9867             }
9868             
9869             /* width is done in CSS
9870              *if(typeof(config.width) != 'undefined'){
9871                 c.style += ' width:' + config.width + 'px;';
9872                 this.totalWidth += config.width;
9873             } else {
9874                 this.totalWidth += 100; // assume minimum of 100 per column?
9875             }
9876             */
9877             
9878             if(typeof(config.cls) != 'undefined'){
9879                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9880             }
9881             // this is the bit that doesnt reall work at all...
9882             
9883             if (this.responsive) {
9884                  
9885             
9886                 ['xs','sm','md','lg'].map(function(size){
9887                     
9888                     if(typeof(config[size]) == 'undefined'){
9889                         return;
9890                     }
9891                      
9892                     if (!config[size]) { // 0 = hidden
9893                         // BS 4 '0' is treated as hide that column and below.
9894                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9895                         return;
9896                     }
9897                     
9898                     c.cls += ' col-' + size + '-' + config[size] + (
9899                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9900                     );
9901                     
9902                     
9903                 });
9904             }
9905             // at the end?
9906             
9907             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9908             
9909             
9910             
9911             
9912             header.cn.push(c)
9913         }
9914         
9915         return header;
9916     },
9917     
9918     renderBody : function()
9919     {
9920         var body = {
9921             tag: 'tbody',
9922             cn : [
9923                 {
9924                     tag: 'tr',
9925                     cn : [
9926                         {
9927                             tag : 'td',
9928                             colspan :  this.cm.getColumnCount()
9929                         }
9930                     ]
9931                 }
9932             ]
9933         };
9934         
9935         return body;
9936     },
9937     
9938     renderFooter : function()
9939     {
9940         var footer = {
9941             tag: 'tfoot',
9942             cn : [
9943                 {
9944                     tag: 'tr',
9945                     cn : [
9946                         {
9947                             tag : 'td',
9948                             colspan :  this.cm.getColumnCount()
9949                         }
9950                     ]
9951                 }
9952             ]
9953         };
9954         
9955         return footer;
9956     },
9957     
9958     
9959     
9960     onLoad : function()
9961     {
9962 //        Roo.log('ds onload');
9963         this.clear();
9964         
9965         var _this = this;
9966         var cm = this.cm;
9967         var ds = this.store;
9968         
9969         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9970             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9971             if (_this.store.sortInfo) {
9972                     
9973                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9974                     e.select('i', true).addClass(['fa-arrow-up']);
9975                 }
9976                 
9977                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9978                     e.select('i', true).addClass(['fa-arrow-down']);
9979                 }
9980             }
9981         });
9982         
9983         var tbody =  this.bodyEl;
9984               
9985         if(ds.getCount() > 0){
9986             ds.data.each(function(d,rowIndex){
9987                 var row =  this.renderRow(cm, ds, rowIndex);
9988                 
9989                 tbody.createChild(row);
9990                 
9991                 var _this = this;
9992                 
9993                 if(row.cellObjects.length){
9994                     Roo.each(row.cellObjects, function(r){
9995                         _this.renderCellObject(r);
9996                     })
9997                 }
9998                 
9999             }, this);
10000         }
10001         
10002         var tfoot = this.el.select('tfoot', true).first();
10003         
10004         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10005             
10006             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10007             
10008             var total = this.ds.getTotalCount();
10009             
10010             if(this.footer.pageSize < total){
10011                 this.mainFoot.show();
10012             }
10013         }
10014         
10015         Roo.each(this.el.select('tbody td', true).elements, function(e){
10016             e.on('mouseover', _this.onMouseover, _this);
10017         });
10018         
10019         Roo.each(this.el.select('tbody td', true).elements, function(e){
10020             e.on('mouseout', _this.onMouseout, _this);
10021         });
10022         this.fireEvent('rowsrendered', this);
10023         
10024         this.autoSize();
10025         
10026         this.initCSS(); /// resize cols
10027
10028         
10029     },
10030     
10031     
10032     onUpdate : function(ds,record)
10033     {
10034         this.refreshRow(record);
10035         this.autoSize();
10036     },
10037     
10038     onRemove : function(ds, record, index, isUpdate){
10039         if(isUpdate !== true){
10040             this.fireEvent("beforerowremoved", this, index, record);
10041         }
10042         var bt = this.bodyEl.dom;
10043         
10044         var rows = this.el.select('tbody > tr', true).elements;
10045         
10046         if(typeof(rows[index]) != 'undefined'){
10047             bt.removeChild(rows[index].dom);
10048         }
10049         
10050 //        if(bt.rows[index]){
10051 //            bt.removeChild(bt.rows[index]);
10052 //        }
10053         
10054         if(isUpdate !== true){
10055             //this.stripeRows(index);
10056             //this.syncRowHeights(index, index);
10057             //this.layout();
10058             this.fireEvent("rowremoved", this, index, record);
10059         }
10060     },
10061     
10062     onAdd : function(ds, records, rowIndex)
10063     {
10064         //Roo.log('on Add called');
10065         // - note this does not handle multiple adding very well..
10066         var bt = this.bodyEl.dom;
10067         for (var i =0 ; i < records.length;i++) {
10068             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10069             //Roo.log(records[i]);
10070             //Roo.log(this.store.getAt(rowIndex+i));
10071             this.insertRow(this.store, rowIndex + i, false);
10072             return;
10073         }
10074         
10075     },
10076     
10077     
10078     refreshRow : function(record){
10079         var ds = this.store, index;
10080         if(typeof record == 'number'){
10081             index = record;
10082             record = ds.getAt(index);
10083         }else{
10084             index = ds.indexOf(record);
10085             if (index < 0) {
10086                 return; // should not happen - but seems to 
10087             }
10088         }
10089         this.insertRow(ds, index, true);
10090         this.autoSize();
10091         this.onRemove(ds, record, index+1, true);
10092         this.autoSize();
10093         //this.syncRowHeights(index, index);
10094         //this.layout();
10095         this.fireEvent("rowupdated", this, index, record);
10096     },
10097     // private - called by RowSelection
10098     onRowSelect : function(rowIndex){
10099         var row = this.getRowDom(rowIndex);
10100         row.addClass(['bg-info','info']);
10101     },
10102     // private - called by RowSelection
10103     onRowDeselect : function(rowIndex)
10104     {
10105         if (rowIndex < 0) {
10106             return;
10107         }
10108         var row = this.getRowDom(rowIndex);
10109         row.removeClass(['bg-info','info']);
10110     },
10111       /**
10112      * Focuses the specified row.
10113      * @param {Number} row The row index
10114      */
10115     focusRow : function(row)
10116     {
10117         //Roo.log('GridView.focusRow');
10118         var x = this.bodyEl.dom.scrollLeft;
10119         this.focusCell(row, 0, false);
10120         this.bodyEl.dom.scrollLeft = x;
10121
10122     },
10123      /**
10124      * Focuses the specified cell.
10125      * @param {Number} row The row index
10126      * @param {Number} col The column index
10127      * @param {Boolean} hscroll false to disable horizontal scrolling
10128      */
10129     focusCell : function(row, col, hscroll)
10130     {
10131         //Roo.log('GridView.focusCell');
10132         var el = this.ensureVisible(row, col, hscroll);
10133         // not sure what focusEL achives = it's a <a> pos relative 
10134         //this.focusEl.alignTo(el, "tl-tl");
10135         //if(Roo.isGecko){
10136         //    this.focusEl.focus();
10137         //}else{
10138         //    this.focusEl.focus.defer(1, this.focusEl);
10139         //}
10140     },
10141     
10142      /**
10143      * Scrolls the specified cell into view
10144      * @param {Number} row The row index
10145      * @param {Number} col The column index
10146      * @param {Boolean} hscroll false to disable horizontal scrolling
10147      */
10148     ensureVisible : function(row, col, hscroll)
10149     {
10150         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10151         //return null; //disable for testing.
10152         if(typeof row != "number"){
10153             row = row.rowIndex;
10154         }
10155         if(row < 0 && row >= this.ds.getCount()){
10156             return  null;
10157         }
10158         col = (col !== undefined ? col : 0);
10159         var cm = this.cm;
10160         while(cm.isHidden(col)){
10161             col++;
10162         }
10163
10164         var el = this.getCellDom(row, col);
10165         if(!el){
10166             return null;
10167         }
10168         var c = this.bodyEl.dom;
10169
10170         var ctop = parseInt(el.offsetTop, 10);
10171         var cleft = parseInt(el.offsetLeft, 10);
10172         var cbot = ctop + el.offsetHeight;
10173         var cright = cleft + el.offsetWidth;
10174
10175         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10176         var ch = 0; //?? header is not withing the area?
10177         var stop = parseInt(c.scrollTop, 10);
10178         var sleft = parseInt(c.scrollLeft, 10);
10179         var sbot = stop + ch;
10180         var sright = sleft + c.clientWidth;
10181         /*
10182         Roo.log('GridView.ensureVisible:' +
10183                 ' ctop:' + ctop +
10184                 ' c.clientHeight:' + c.clientHeight +
10185                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10186                 ' stop:' + stop +
10187                 ' cbot:' + cbot +
10188                 ' sbot:' + sbot +
10189                 ' ch:' + ch  
10190                 );
10191         */
10192         if(ctop < stop){
10193             c.scrollTop = ctop;
10194             //Roo.log("set scrolltop to ctop DISABLE?");
10195         }else if(cbot > sbot){
10196             //Roo.log("set scrolltop to cbot-ch");
10197             c.scrollTop = cbot-ch;
10198         }
10199
10200         if(hscroll !== false){
10201             if(cleft < sleft){
10202                 c.scrollLeft = cleft;
10203             }else if(cright > sright){
10204                 c.scrollLeft = cright-c.clientWidth;
10205             }
10206         }
10207
10208         return el;
10209     },
10210     
10211     
10212     insertRow : function(dm, rowIndex, isUpdate){
10213         
10214         if(!isUpdate){
10215             this.fireEvent("beforerowsinserted", this, rowIndex);
10216         }
10217             //var s = this.getScrollState();
10218         var row = this.renderRow(this.cm, this.store, rowIndex);
10219         // insert before rowIndex..
10220         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10221         
10222         var _this = this;
10223                 
10224         if(row.cellObjects.length){
10225             Roo.each(row.cellObjects, function(r){
10226                 _this.renderCellObject(r);
10227             })
10228         }
10229             
10230         if(!isUpdate){
10231             this.fireEvent("rowsinserted", this, rowIndex);
10232             //this.syncRowHeights(firstRow, lastRow);
10233             //this.stripeRows(firstRow);
10234             //this.layout();
10235         }
10236         
10237     },
10238     
10239     
10240     getRowDom : function(rowIndex)
10241     {
10242         var rows = this.el.select('tbody > tr', true).elements;
10243         
10244         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10245         
10246     },
10247     getCellDom : function(rowIndex, colIndex)
10248     {
10249         var row = this.getRowDom(rowIndex);
10250         if (row === false) {
10251             return false;
10252         }
10253         var cols = row.select('td', true).elements;
10254         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10255         
10256     },
10257     
10258     // returns the object tree for a tr..
10259   
10260     
10261     renderRow : function(cm, ds, rowIndex) 
10262     {
10263         var d = ds.getAt(rowIndex);
10264         
10265         var row = {
10266             tag : 'tr',
10267             cls : 'x-row-' + rowIndex,
10268             cn : []
10269         };
10270             
10271         var cellObjects = [];
10272         
10273         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10274             var config = cm.config[i];
10275             
10276             var renderer = cm.getRenderer(i);
10277             var value = '';
10278             var id = false;
10279             
10280             if(typeof(renderer) !== 'undefined'){
10281                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10282             }
10283             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10284             // and are rendered into the cells after the row is rendered - using the id for the element.
10285             
10286             if(typeof(value) === 'object'){
10287                 id = Roo.id();
10288                 cellObjects.push({
10289                     container : id,
10290                     cfg : value 
10291                 })
10292             }
10293             
10294             var rowcfg = {
10295                 record: d,
10296                 rowIndex : rowIndex,
10297                 colIndex : i,
10298                 rowClass : ''
10299             };
10300
10301             this.fireEvent('rowclass', this, rowcfg);
10302             
10303             var td = {
10304                 tag: 'td',
10305                 // this might end up displaying HTML?
10306                 // this is too messy... - better to only do it on columsn you know are going to be too long
10307                 //tooltip : (typeof(value) === 'object') ? '' : value,
10308                 cls : rowcfg.rowClass + ' x-col-' + i,
10309                 style: '',
10310                 html: (typeof(value) === 'object') ? '' : value
10311             };
10312             
10313             if (id) {
10314                 td.id = id;
10315             }
10316             
10317             if(typeof(config.colspan) != 'undefined'){
10318                 td.colspan = config.colspan;
10319             }
10320             
10321             
10322             
10323             if(typeof(config.align) != 'undefined' && config.align.length){
10324                 td.style += ' text-align:' + config.align + ';';
10325             }
10326             if(typeof(config.valign) != 'undefined' && config.valign.length){
10327                 td.style += ' vertical-align:' + config.valign + ';';
10328             }
10329             /*
10330             if(typeof(config.width) != 'undefined'){
10331                 td.style += ' width:' +  config.width + 'px;';
10332             }
10333             */
10334             
10335             if(typeof(config.cursor) != 'undefined'){
10336                 td.style += ' cursor:' +  config.cursor + ';';
10337             }
10338             
10339             if(typeof(config.cls) != 'undefined'){
10340                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10341             }
10342             if (this.responsive) {
10343                 ['xs','sm','md','lg'].map(function(size){
10344                     
10345                     if(typeof(config[size]) == 'undefined'){
10346                         return;
10347                     }
10348                     
10349                     
10350                       
10351                     if (!config[size]) { // 0 = hidden
10352                         // BS 4 '0' is treated as hide that column and below.
10353                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10354                         return;
10355                     }
10356                     
10357                     td.cls += ' col-' + size + '-' + config[size] + (
10358                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10359                     );
10360                      
10361     
10362                 });
10363             }
10364             row.cn.push(td);
10365            
10366         }
10367         
10368         row.cellObjects = cellObjects;
10369         
10370         return row;
10371           
10372     },
10373     
10374     
10375     
10376     onBeforeLoad : function()
10377     {
10378         
10379     },
10380      /**
10381      * Remove all rows
10382      */
10383     clear : function()
10384     {
10385         this.el.select('tbody', true).first().dom.innerHTML = '';
10386     },
10387     /**
10388      * Show or hide a row.
10389      * @param {Number} rowIndex to show or hide
10390      * @param {Boolean} state hide
10391      */
10392     setRowVisibility : function(rowIndex, state)
10393     {
10394         var bt = this.bodyEl.dom;
10395         
10396         var rows = this.el.select('tbody > tr', true).elements;
10397         
10398         if(typeof(rows[rowIndex]) == 'undefined'){
10399             return;
10400         }
10401         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10402         
10403     },
10404     
10405     
10406     getSelectionModel : function(){
10407         if(!this.selModel){
10408             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10409         }
10410         return this.selModel;
10411     },
10412     /*
10413      * Render the Roo.bootstrap object from renderder
10414      */
10415     renderCellObject : function(r)
10416     {
10417         var _this = this;
10418         
10419         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10420         
10421         var t = r.cfg.render(r.container);
10422         
10423         if(r.cfg.cn){
10424             Roo.each(r.cfg.cn, function(c){
10425                 var child = {
10426                     container: t.getChildContainer(),
10427                     cfg: c
10428                 };
10429                 _this.renderCellObject(child);
10430             })
10431         }
10432     },
10433     /**
10434      * get the Row Index from a dom element.
10435      * @param {Roo.Element} row The row to look for
10436      * @returns {Number} the row
10437      */
10438     getRowIndex : function(row)
10439     {
10440         var rowIndex = -1;
10441         
10442         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10443             if(el != row){
10444                 return;
10445             }
10446             
10447             rowIndex = index;
10448         });
10449         
10450         return rowIndex;
10451     },
10452     /**
10453      * get the header TH element for columnIndex
10454      * @param {Number} columnIndex
10455      * @returns {Roo.Element}
10456      */
10457     getHeaderIndex: function(colIndex)
10458     {
10459         var cols = this.headEl.select('th', true).elements;
10460         return cols[colIndex]; 
10461     },
10462     /**
10463      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10464      * @param {domElement} cell to look for
10465      * @returns {Number} the column
10466      */
10467     getCellIndex : function(cell)
10468     {
10469         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10470         if(id){
10471             return parseInt(id[1], 10);
10472         }
10473         return 0;
10474     },
10475      /**
10476      * Returns the grid's underlying element = used by panel.Grid
10477      * @return {Element} The element
10478      */
10479     getGridEl : function(){
10480         return this.el;
10481     },
10482      /**
10483      * Forces a resize - used by panel.Grid
10484      * @return {Element} The element
10485      */
10486     autoSize : function()
10487     {
10488         //var ctr = Roo.get(this.container.dom.parentElement);
10489         var ctr = Roo.get(this.el.dom);
10490         
10491         var thd = this.getGridEl().select('thead',true).first();
10492         var tbd = this.getGridEl().select('tbody', true).first();
10493         var tfd = this.getGridEl().select('tfoot', true).first();
10494         
10495         var cw = ctr.getWidth();
10496         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10497         
10498         if (tbd) {
10499             
10500             tbd.setWidth(ctr.getWidth());
10501             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10502             // this needs fixing for various usage - currently only hydra job advers I think..
10503             //tdb.setHeight(
10504             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10505             //); 
10506             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10507             cw -= barsize;
10508         }
10509         cw = Math.max(cw, this.totalWidth);
10510         this.getGridEl().select('tbody tr',true).setWidth(cw);
10511         this.initCSS();
10512         
10513         // resize 'expandable coloumn?
10514         
10515         return; // we doe not have a view in this design..
10516         
10517     },
10518     onBodyScroll: function()
10519     {
10520         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10521         if(this.headEl){
10522             this.headEl.setStyle({
10523                 'position' : 'relative',
10524                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10525             });
10526         }
10527         
10528         if(this.lazyLoad){
10529             
10530             var scrollHeight = this.bodyEl.dom.scrollHeight;
10531             
10532             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10533             
10534             var height = this.bodyEl.getHeight();
10535             
10536             if(scrollHeight - height == scrollTop) {
10537                 
10538                 var total = this.ds.getTotalCount();
10539                 
10540                 if(this.footer.cursor + this.footer.pageSize < total){
10541                     
10542                     this.footer.ds.load({
10543                         params : {
10544                             start : this.footer.cursor + this.footer.pageSize,
10545                             limit : this.footer.pageSize
10546                         },
10547                         add : true
10548                     });
10549                 }
10550             }
10551             
10552         }
10553     },
10554     onColumnSplitterMoved : function(i, diff)
10555     {
10556         this.userResized = true;
10557         
10558         var cm = this.colModel;
10559         
10560         var w = this.getHeaderIndex(i).getWidth() + diff;
10561         
10562         
10563         cm.setColumnWidth(i, w, true);
10564         this.initCSS();
10565         //var cid = cm.getColumnId(i); << not used in this version?
10566        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10567         
10568         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10569         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10570         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10571 */
10572         //this.updateSplitters();
10573         //this.layout(); << ??
10574         this.fireEvent("columnresize", i, w);
10575     },
10576     onHeaderChange : function()
10577     {
10578         var header = this.renderHeader();
10579         var table = this.el.select('table', true).first();
10580         
10581         this.headEl.remove();
10582         this.headEl = table.createChild(header, this.bodyEl, false);
10583         
10584         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10585             e.on('click', this.sort, this);
10586         }, this);
10587         
10588         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10589             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10590         }
10591         
10592     },
10593     
10594     onHiddenChange : function(colModel, colIndex, hidden)
10595     {
10596         /*
10597         this.cm.setHidden()
10598         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10599         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10600         
10601         this.CSS.updateRule(thSelector, "display", "");
10602         this.CSS.updateRule(tdSelector, "display", "");
10603         
10604         if(hidden){
10605             this.CSS.updateRule(thSelector, "display", "none");
10606             this.CSS.updateRule(tdSelector, "display", "none");
10607         }
10608         */
10609         // onload calls initCSS()
10610         this.onHeaderChange();
10611         this.onLoad();
10612     },
10613     
10614     setColumnWidth: function(col_index, width)
10615     {
10616         // width = "md-2 xs-2..."
10617         if(!this.colModel.config[col_index]) {
10618             return;
10619         }
10620         
10621         var w = width.split(" ");
10622         
10623         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10624         
10625         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10626         
10627         
10628         for(var j = 0; j < w.length; j++) {
10629             
10630             if(!w[j]) {
10631                 continue;
10632             }
10633             
10634             var size_cls = w[j].split("-");
10635             
10636             if(!Number.isInteger(size_cls[1] * 1)) {
10637                 continue;
10638             }
10639             
10640             if(!this.colModel.config[col_index][size_cls[0]]) {
10641                 continue;
10642             }
10643             
10644             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10645                 continue;
10646             }
10647             
10648             h_row[0].classList.replace(
10649                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10650                 "col-"+size_cls[0]+"-"+size_cls[1]
10651             );
10652             
10653             for(var i = 0; i < rows.length; i++) {
10654                 
10655                 var size_cls = w[j].split("-");
10656                 
10657                 if(!Number.isInteger(size_cls[1] * 1)) {
10658                     continue;
10659                 }
10660                 
10661                 if(!this.colModel.config[col_index][size_cls[0]]) {
10662                     continue;
10663                 }
10664                 
10665                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10666                     continue;
10667                 }
10668                 
10669                 rows[i].classList.replace(
10670                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10671                     "col-"+size_cls[0]+"-"+size_cls[1]
10672                 );
10673             }
10674             
10675             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10676         }
10677     }
10678 });
10679
10680 // currently only used to find the split on drag.. 
10681 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10682
10683 /**
10684  * @depricated
10685 */
10686 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10687 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10688 /*
10689  * - LGPL
10690  *
10691  * table cell
10692  * 
10693  */
10694
10695 /**
10696  * @class Roo.bootstrap.TableCell
10697  * @extends Roo.bootstrap.Component
10698  * @children Roo.bootstrap.Component
10699  * @parent Roo.bootstrap.TableRow
10700  * Bootstrap TableCell class
10701  * 
10702  * @cfg {String} html cell contain text
10703  * @cfg {String} cls cell class
10704  * @cfg {String} tag cell tag (td|th) default td
10705  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10706  * @cfg {String} align Aligns the content in a cell
10707  * @cfg {String} axis Categorizes cells
10708  * @cfg {String} bgcolor Specifies the background color of a cell
10709  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10710  * @cfg {Number} colspan Specifies the number of columns a cell should span
10711  * @cfg {String} headers Specifies one or more header cells a cell is related to
10712  * @cfg {Number} height Sets the height of a cell
10713  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10714  * @cfg {Number} rowspan Sets the number of rows a cell should span
10715  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10716  * @cfg {String} valign Vertical aligns the content in a cell
10717  * @cfg {Number} width Specifies the width of a cell
10718  * 
10719  * @constructor
10720  * Create a new TableCell
10721  * @param {Object} config The config object
10722  */
10723
10724 Roo.bootstrap.TableCell = function(config){
10725     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10726 };
10727
10728 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10729     
10730     html: false,
10731     cls: false,
10732     tag: false,
10733     abbr: false,
10734     align: false,
10735     axis: false,
10736     bgcolor: false,
10737     charoff: false,
10738     colspan: false,
10739     headers: false,
10740     height: false,
10741     nowrap: false,
10742     rowspan: false,
10743     scope: false,
10744     valign: false,
10745     width: false,
10746     
10747     
10748     getAutoCreate : function(){
10749         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10750         
10751         cfg = {
10752             tag: 'td'
10753         };
10754         
10755         if(this.tag){
10756             cfg.tag = this.tag;
10757         }
10758         
10759         if (this.html) {
10760             cfg.html=this.html
10761         }
10762         if (this.cls) {
10763             cfg.cls=this.cls
10764         }
10765         if (this.abbr) {
10766             cfg.abbr=this.abbr
10767         }
10768         if (this.align) {
10769             cfg.align=this.align
10770         }
10771         if (this.axis) {
10772             cfg.axis=this.axis
10773         }
10774         if (this.bgcolor) {
10775             cfg.bgcolor=this.bgcolor
10776         }
10777         if (this.charoff) {
10778             cfg.charoff=this.charoff
10779         }
10780         if (this.colspan) {
10781             cfg.colspan=this.colspan
10782         }
10783         if (this.headers) {
10784             cfg.headers=this.headers
10785         }
10786         if (this.height) {
10787             cfg.height=this.height
10788         }
10789         if (this.nowrap) {
10790             cfg.nowrap=this.nowrap
10791         }
10792         if (this.rowspan) {
10793             cfg.rowspan=this.rowspan
10794         }
10795         if (this.scope) {
10796             cfg.scope=this.scope
10797         }
10798         if (this.valign) {
10799             cfg.valign=this.valign
10800         }
10801         if (this.width) {
10802             cfg.width=this.width
10803         }
10804         
10805         
10806         return cfg;
10807     }
10808    
10809 });
10810
10811  
10812
10813  /*
10814  * - LGPL
10815  *
10816  * table row
10817  * 
10818  */
10819
10820 /**
10821  * @class Roo.bootstrap.TableRow
10822  * @extends Roo.bootstrap.Component
10823  * @children Roo.bootstrap.TableCell
10824  * @parent Roo.bootstrap.TableBody
10825  * Bootstrap TableRow class
10826  * @cfg {String} cls row class
10827  * @cfg {String} align Aligns the content in a table row
10828  * @cfg {String} bgcolor Specifies a background color for a table row
10829  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10830  * @cfg {String} valign Vertical aligns the content in a table row
10831  * 
10832  * @constructor
10833  * Create a new TableRow
10834  * @param {Object} config The config object
10835  */
10836
10837 Roo.bootstrap.TableRow = function(config){
10838     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10839 };
10840
10841 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10842     
10843     cls: false,
10844     align: false,
10845     bgcolor: false,
10846     charoff: false,
10847     valign: false,
10848     
10849     getAutoCreate : function(){
10850         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10851         
10852         cfg = {
10853             tag: 'tr'
10854         };
10855             
10856         if(this.cls){
10857             cfg.cls = this.cls;
10858         }
10859         if(this.align){
10860             cfg.align = this.align;
10861         }
10862         if(this.bgcolor){
10863             cfg.bgcolor = this.bgcolor;
10864         }
10865         if(this.charoff){
10866             cfg.charoff = this.charoff;
10867         }
10868         if(this.valign){
10869             cfg.valign = this.valign;
10870         }
10871         
10872         return cfg;
10873     }
10874    
10875 });
10876
10877  
10878
10879  /*
10880  * - LGPL
10881  *
10882  * table body
10883  * 
10884  */
10885
10886 /**
10887  * @class Roo.bootstrap.TableBody
10888  * @extends Roo.bootstrap.Component
10889  * @children Roo.bootstrap.TableRow
10890  * @parent Roo.bootstrap.Table
10891  * Bootstrap TableBody class
10892  * @cfg {String} cls element class
10893  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10894  * @cfg {String} align Aligns the content inside the element
10895  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10896  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10897  * 
10898  * @constructor
10899  * Create a new TableBody
10900  * @param {Object} config The config object
10901  */
10902
10903 Roo.bootstrap.TableBody = function(config){
10904     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10905 };
10906
10907 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10908     
10909     cls: false,
10910     tag: false,
10911     align: false,
10912     charoff: false,
10913     valign: false,
10914     
10915     getAutoCreate : function(){
10916         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10917         
10918         cfg = {
10919             tag: 'tbody'
10920         };
10921             
10922         if (this.cls) {
10923             cfg.cls=this.cls
10924         }
10925         if(this.tag){
10926             cfg.tag = this.tag;
10927         }
10928         
10929         if(this.align){
10930             cfg.align = this.align;
10931         }
10932         if(this.charoff){
10933             cfg.charoff = this.charoff;
10934         }
10935         if(this.valign){
10936             cfg.valign = this.valign;
10937         }
10938         
10939         return cfg;
10940     }
10941     
10942     
10943 //    initEvents : function()
10944 //    {
10945 //        
10946 //        if(!this.store){
10947 //            return;
10948 //        }
10949 //        
10950 //        this.store = Roo.factory(this.store, Roo.data);
10951 //        this.store.on('load', this.onLoad, this);
10952 //        
10953 //        this.store.load();
10954 //        
10955 //    },
10956 //    
10957 //    onLoad: function () 
10958 //    {   
10959 //        this.fireEvent('load', this);
10960 //    }
10961 //    
10962 //   
10963 });
10964
10965  
10966
10967  /*
10968  * Based on:
10969  * Ext JS Library 1.1.1
10970  * Copyright(c) 2006-2007, Ext JS, LLC.
10971  *
10972  * Originally Released Under LGPL - original licence link has changed is not relivant.
10973  *
10974  * Fork - LGPL
10975  * <script type="text/javascript">
10976  */
10977
10978 // as we use this in bootstrap.
10979 Roo.namespace('Roo.form');
10980  /**
10981  * @class Roo.form.Action
10982  * Internal Class used to handle form actions
10983  * @constructor
10984  * @param {Roo.form.BasicForm} el The form element or its id
10985  * @param {Object} config Configuration options
10986  */
10987
10988  
10989  
10990 // define the action interface
10991 Roo.form.Action = function(form, options){
10992     this.form = form;
10993     this.options = options || {};
10994 };
10995 /**
10996  * Client Validation Failed
10997  * @const 
10998  */
10999 Roo.form.Action.CLIENT_INVALID = 'client';
11000 /**
11001  * Server Validation Failed
11002  * @const 
11003  */
11004 Roo.form.Action.SERVER_INVALID = 'server';
11005  /**
11006  * Connect to Server Failed
11007  * @const 
11008  */
11009 Roo.form.Action.CONNECT_FAILURE = 'connect';
11010 /**
11011  * Reading Data from Server Failed
11012  * @const 
11013  */
11014 Roo.form.Action.LOAD_FAILURE = 'load';
11015
11016 Roo.form.Action.prototype = {
11017     type : 'default',
11018     failureType : undefined,
11019     response : undefined,
11020     result : undefined,
11021
11022     // interface method
11023     run : function(options){
11024
11025     },
11026
11027     // interface method
11028     success : function(response){
11029
11030     },
11031
11032     // interface method
11033     handleResponse : function(response){
11034
11035     },
11036
11037     // default connection failure
11038     failure : function(response){
11039         
11040         this.response = response;
11041         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11042         this.form.afterAction(this, false);
11043     },
11044
11045     processResponse : function(response){
11046         this.response = response;
11047         if(!response.responseText){
11048             return true;
11049         }
11050         this.result = this.handleResponse(response);
11051         return this.result;
11052     },
11053
11054     // utility functions used internally
11055     getUrl : function(appendParams){
11056         var url = this.options.url || this.form.url || this.form.el.dom.action;
11057         if(appendParams){
11058             var p = this.getParams();
11059             if(p){
11060                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11061             }
11062         }
11063         return url;
11064     },
11065
11066     getMethod : function(){
11067         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11068     },
11069
11070     getParams : function(){
11071         var bp = this.form.baseParams;
11072         var p = this.options.params;
11073         if(p){
11074             if(typeof p == "object"){
11075                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11076             }else if(typeof p == 'string' && bp){
11077                 p += '&' + Roo.urlEncode(bp);
11078             }
11079         }else if(bp){
11080             p = Roo.urlEncode(bp);
11081         }
11082         return p;
11083     },
11084
11085     createCallback : function(){
11086         return {
11087             success: this.success,
11088             failure: this.failure,
11089             scope: this,
11090             timeout: (this.form.timeout*1000),
11091             upload: this.form.fileUpload ? this.success : undefined
11092         };
11093     }
11094 };
11095
11096 Roo.form.Action.Submit = function(form, options){
11097     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11098 };
11099
11100 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11101     type : 'submit',
11102
11103     haveProgress : false,
11104     uploadComplete : false,
11105     
11106     // uploadProgress indicator.
11107     uploadProgress : function()
11108     {
11109         if (!this.form.progressUrl) {
11110             return;
11111         }
11112         
11113         if (!this.haveProgress) {
11114             Roo.MessageBox.progress("Uploading", "Uploading");
11115         }
11116         if (this.uploadComplete) {
11117            Roo.MessageBox.hide();
11118            return;
11119         }
11120         
11121         this.haveProgress = true;
11122    
11123         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11124         
11125         var c = new Roo.data.Connection();
11126         c.request({
11127             url : this.form.progressUrl,
11128             params: {
11129                 id : uid
11130             },
11131             method: 'GET',
11132             success : function(req){
11133                //console.log(data);
11134                 var rdata = false;
11135                 var edata;
11136                 try  {
11137                    rdata = Roo.decode(req.responseText)
11138                 } catch (e) {
11139                     Roo.log("Invalid data from server..");
11140                     Roo.log(edata);
11141                     return;
11142                 }
11143                 if (!rdata || !rdata.success) {
11144                     Roo.log(rdata);
11145                     Roo.MessageBox.alert(Roo.encode(rdata));
11146                     return;
11147                 }
11148                 var data = rdata.data;
11149                 
11150                 if (this.uploadComplete) {
11151                    Roo.MessageBox.hide();
11152                    return;
11153                 }
11154                    
11155                 if (data){
11156                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11157                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11158                     );
11159                 }
11160                 this.uploadProgress.defer(2000,this);
11161             },
11162        
11163             failure: function(data) {
11164                 Roo.log('progress url failed ');
11165                 Roo.log(data);
11166             },
11167             scope : this
11168         });
11169            
11170     },
11171     
11172     
11173     run : function()
11174     {
11175         // run get Values on the form, so it syncs any secondary forms.
11176         this.form.getValues();
11177         
11178         var o = this.options;
11179         var method = this.getMethod();
11180         var isPost = method == 'POST';
11181         if(o.clientValidation === false || this.form.isValid()){
11182             
11183             if (this.form.progressUrl) {
11184                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11185                     (new Date() * 1) + '' + Math.random());
11186                     
11187             } 
11188             
11189             
11190             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11191                 form:this.form.el.dom,
11192                 url:this.getUrl(!isPost),
11193                 method: method,
11194                 params:isPost ? this.getParams() : null,
11195                 isUpload: this.form.fileUpload,
11196                 formData : this.form.formData
11197             }));
11198             
11199             this.uploadProgress();
11200
11201         }else if (o.clientValidation !== false){ // client validation failed
11202             this.failureType = Roo.form.Action.CLIENT_INVALID;
11203             this.form.afterAction(this, false);
11204         }
11205     },
11206
11207     success : function(response)
11208     {
11209         this.uploadComplete= true;
11210         if (this.haveProgress) {
11211             Roo.MessageBox.hide();
11212         }
11213         
11214         
11215         var result = this.processResponse(response);
11216         if(result === true || result.success){
11217             this.form.afterAction(this, true);
11218             return;
11219         }
11220         if(result.errors){
11221             this.form.markInvalid(result.errors);
11222             this.failureType = Roo.form.Action.SERVER_INVALID;
11223         }
11224         this.form.afterAction(this, false);
11225     },
11226     failure : function(response)
11227     {
11228         this.uploadComplete= true;
11229         if (this.haveProgress) {
11230             Roo.MessageBox.hide();
11231         }
11232         
11233         this.response = response;
11234         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11235         this.form.afterAction(this, false);
11236     },
11237     
11238     handleResponse : function(response){
11239         if(this.form.errorReader){
11240             var rs = this.form.errorReader.read(response);
11241             var errors = [];
11242             if(rs.records){
11243                 for(var i = 0, len = rs.records.length; i < len; i++) {
11244                     var r = rs.records[i];
11245                     errors[i] = r.data;
11246                 }
11247             }
11248             if(errors.length < 1){
11249                 errors = null;
11250             }
11251             return {
11252                 success : rs.success,
11253                 errors : errors
11254             };
11255         }
11256         var ret = false;
11257         try {
11258             ret = Roo.decode(response.responseText);
11259         } catch (e) {
11260             ret = {
11261                 success: false,
11262                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11263                 errors : []
11264             };
11265         }
11266         return ret;
11267         
11268     }
11269 });
11270
11271
11272 Roo.form.Action.Load = function(form, options){
11273     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11274     this.reader = this.form.reader;
11275 };
11276
11277 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11278     type : 'load',
11279
11280     run : function(){
11281         
11282         Roo.Ajax.request(Roo.apply(
11283                 this.createCallback(), {
11284                     method:this.getMethod(),
11285                     url:this.getUrl(false),
11286                     params:this.getParams()
11287         }));
11288     },
11289
11290     success : function(response){
11291         
11292         var result = this.processResponse(response);
11293         if(result === true || !result.success || !result.data){
11294             this.failureType = Roo.form.Action.LOAD_FAILURE;
11295             this.form.afterAction(this, false);
11296             return;
11297         }
11298         this.form.clearInvalid();
11299         this.form.setValues(result.data);
11300         this.form.afterAction(this, true);
11301     },
11302
11303     handleResponse : function(response){
11304         if(this.form.reader){
11305             var rs = this.form.reader.read(response);
11306             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11307             return {
11308                 success : rs.success,
11309                 data : data
11310             };
11311         }
11312         return Roo.decode(response.responseText);
11313     }
11314 });
11315
11316 Roo.form.Action.ACTION_TYPES = {
11317     'load' : Roo.form.Action.Load,
11318     'submit' : Roo.form.Action.Submit
11319 };/*
11320  * - LGPL
11321  *
11322  * form
11323  *
11324  */
11325
11326 /**
11327  * @class Roo.bootstrap.form.Form
11328  * @extends Roo.bootstrap.Component
11329  * @children Roo.bootstrap.Component
11330  * Bootstrap Form class
11331  * @cfg {String} method  GET | POST (default POST)
11332  * @cfg {String} labelAlign top | left (default top)
11333  * @cfg {String} align left  | right - for navbars
11334  * @cfg {Boolean} loadMask load mask when submit (default true)
11335
11336  *
11337  * @constructor
11338  * Create a new Form
11339  * @param {Object} config The config object
11340  */
11341
11342
11343 Roo.bootstrap.form.Form = function(config){
11344     
11345     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11346     
11347     Roo.bootstrap.form.Form.popover.apply();
11348     
11349     this.addEvents({
11350         /**
11351          * @event clientvalidation
11352          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11353          * @param {Form} this
11354          * @param {Boolean} valid true if the form has passed client-side validation
11355          */
11356         clientvalidation: true,
11357         /**
11358          * @event beforeaction
11359          * Fires before any action is performed. Return false to cancel the action.
11360          * @param {Form} this
11361          * @param {Action} action The action to be performed
11362          */
11363         beforeaction: true,
11364         /**
11365          * @event actionfailed
11366          * Fires when an action fails.
11367          * @param {Form} this
11368          * @param {Action} action The action that failed
11369          */
11370         actionfailed : true,
11371         /**
11372          * @event actioncomplete
11373          * Fires when an action is completed.
11374          * @param {Form} this
11375          * @param {Action} action The action that completed
11376          */
11377         actioncomplete : true
11378     });
11379 };
11380
11381 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11382
11383      /**
11384      * @cfg {String} method
11385      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11386      */
11387     method : 'POST',
11388     /**
11389      * @cfg {String} url
11390      * The URL to use for form actions if one isn't supplied in the action options.
11391      */
11392     /**
11393      * @cfg {Boolean} fileUpload
11394      * Set to true if this form is a file upload.
11395      */
11396
11397     /**
11398      * @cfg {Object} baseParams
11399      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11400      */
11401
11402     /**
11403      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11404      */
11405     timeout: 30,
11406     /**
11407      * @cfg {Sting} align (left|right) for navbar forms
11408      */
11409     align : 'left',
11410
11411     // private
11412     activeAction : null,
11413
11414     /**
11415      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11416      * element by passing it or its id or mask the form itself by passing in true.
11417      * @type Mixed
11418      */
11419     waitMsgTarget : false,
11420
11421     loadMask : true,
11422     
11423     /**
11424      * @cfg {Boolean} errorMask (true|false) default false
11425      */
11426     errorMask : false,
11427     
11428     /**
11429      * @cfg {Number} maskOffset Default 100
11430      */
11431     maskOffset : 100,
11432     
11433     /**
11434      * @cfg {Boolean} maskBody
11435      */
11436     maskBody : false,
11437
11438     getAutoCreate : function(){
11439
11440         var cfg = {
11441             tag: 'form',
11442             method : this.method || 'POST',
11443             id : this.id || Roo.id(),
11444             cls : ''
11445         };
11446         if (this.parent().xtype.match(/^Nav/)) {
11447             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11448
11449         }
11450
11451         if (this.labelAlign == 'left' ) {
11452             cfg.cls += ' form-horizontal';
11453         }
11454
11455
11456         return cfg;
11457     },
11458     initEvents : function()
11459     {
11460         this.el.on('submit', this.onSubmit, this);
11461         // this was added as random key presses on the form where triggering form submit.
11462         this.el.on('keypress', function(e) {
11463             if (e.getCharCode() != 13) {
11464                 return true;
11465             }
11466             // we might need to allow it for textareas.. and some other items.
11467             // check e.getTarget().
11468
11469             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11470                 return true;
11471             }
11472
11473             Roo.log("keypress blocked");
11474
11475             e.preventDefault();
11476             return false;
11477         });
11478         
11479     },
11480     // private
11481     onSubmit : function(e){
11482         e.stopEvent();
11483     },
11484
11485      /**
11486      * Returns true if client-side validation on the form is successful.
11487      * @return Boolean
11488      */
11489     isValid : function(){
11490         var items = this.getItems();
11491         var valid = true;
11492         var target = false;
11493         
11494         items.each(function(f){
11495             
11496             if(f.validate()){
11497                 return;
11498             }
11499             
11500             Roo.log('invalid field: ' + f.name);
11501             
11502             valid = false;
11503
11504             if(!target && f.el.isVisible(true)){
11505                 target = f;
11506             }
11507            
11508         });
11509         
11510         if(this.errorMask && !valid){
11511             Roo.bootstrap.form.Form.popover.mask(this, target);
11512         }
11513         
11514         return valid;
11515     },
11516     
11517     /**
11518      * Returns true if any fields in this form have changed since their original load.
11519      * @return Boolean
11520      */
11521     isDirty : function(){
11522         var dirty = false;
11523         var items = this.getItems();
11524         items.each(function(f){
11525            if(f.isDirty()){
11526                dirty = true;
11527                return false;
11528            }
11529            return true;
11530         });
11531         return dirty;
11532     },
11533      /**
11534      * Performs a predefined action (submit or load) or custom actions you define on this form.
11535      * @param {String} actionName The name of the action type
11536      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11537      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11538      * accept other config options):
11539      * <pre>
11540 Property          Type             Description
11541 ----------------  ---------------  ----------------------------------------------------------------------------------
11542 url               String           The url for the action (defaults to the form's url)
11543 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11544 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11545 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11546                                    validate the form on the client (defaults to false)
11547      * </pre>
11548      * @return {BasicForm} this
11549      */
11550     doAction : function(action, options){
11551         if(typeof action == 'string'){
11552             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11553         }
11554         if(this.fireEvent('beforeaction', this, action) !== false){
11555             this.beforeAction(action);
11556             action.run.defer(100, action);
11557         }
11558         return this;
11559     },
11560
11561     // private
11562     beforeAction : function(action){
11563         var o = action.options;
11564         
11565         if(this.loadMask){
11566             
11567             if(this.maskBody){
11568                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11569             } else {
11570                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11571             }
11572         }
11573         // not really supported yet.. ??
11574
11575         //if(this.waitMsgTarget === true){
11576         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11577         //}else if(this.waitMsgTarget){
11578         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11579         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11580         //}else {
11581         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11582        // }
11583
11584     },
11585
11586     // private
11587     afterAction : function(action, success){
11588         this.activeAction = null;
11589         var o = action.options;
11590
11591         if(this.loadMask){
11592             
11593             if(this.maskBody){
11594                 Roo.get(document.body).unmask();
11595             } else {
11596                 this.el.unmask();
11597             }
11598         }
11599         
11600         //if(this.waitMsgTarget === true){
11601 //            this.el.unmask();
11602         //}else if(this.waitMsgTarget){
11603         //    this.waitMsgTarget.unmask();
11604         //}else{
11605         //    Roo.MessageBox.updateProgress(1);
11606         //    Roo.MessageBox.hide();
11607        // }
11608         //
11609         if(success){
11610             if(o.reset){
11611                 this.reset();
11612             }
11613             Roo.callback(o.success, o.scope, [this, action]);
11614             this.fireEvent('actioncomplete', this, action);
11615
11616         }else{
11617
11618             // failure condition..
11619             // we have a scenario where updates need confirming.
11620             // eg. if a locking scenario exists..
11621             // we look for { errors : { needs_confirm : true }} in the response.
11622             if (
11623                 (typeof(action.result) != 'undefined')  &&
11624                 (typeof(action.result.errors) != 'undefined')  &&
11625                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11626            ){
11627                 var _t = this;
11628                 Roo.log("not supported yet");
11629                  /*
11630
11631                 Roo.MessageBox.confirm(
11632                     "Change requires confirmation",
11633                     action.result.errorMsg,
11634                     function(r) {
11635                         if (r != 'yes') {
11636                             return;
11637                         }
11638                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11639                     }
11640
11641                 );
11642                 */
11643
11644
11645                 return;
11646             }
11647
11648             Roo.callback(o.failure, o.scope, [this, action]);
11649             // show an error message if no failed handler is set..
11650             if (!this.hasListener('actionfailed')) {
11651                 Roo.log("need to add dialog support");
11652                 /*
11653                 Roo.MessageBox.alert("Error",
11654                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11655                         action.result.errorMsg :
11656                         "Saving Failed, please check your entries or try again"
11657                 );
11658                 */
11659             }
11660
11661             this.fireEvent('actionfailed', this, action);
11662         }
11663
11664     },
11665     /**
11666      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11667      * @param {String} id The value to search for
11668      * @return Field
11669      */
11670     findField : function(id){
11671         var items = this.getItems();
11672         var field = items.get(id);
11673         if(!field){
11674              items.each(function(f){
11675                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11676                     field = f;
11677                     return false;
11678                 }
11679                 return true;
11680             });
11681         }
11682         return field || null;
11683     },
11684      /**
11685      * Mark fields in this form invalid in bulk.
11686      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11687      * @return {BasicForm} this
11688      */
11689     markInvalid : function(errors){
11690         if(errors instanceof Array){
11691             for(var i = 0, len = errors.length; i < len; i++){
11692                 var fieldError = errors[i];
11693                 var f = this.findField(fieldError.id);
11694                 if(f){
11695                     f.markInvalid(fieldError.msg);
11696                 }
11697             }
11698         }else{
11699             var field, id;
11700             for(id in errors){
11701                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11702                     field.markInvalid(errors[id]);
11703                 }
11704             }
11705         }
11706         //Roo.each(this.childForms || [], function (f) {
11707         //    f.markInvalid(errors);
11708         //});
11709
11710         return this;
11711     },
11712
11713     /**
11714      * Set values for fields in this form in bulk.
11715      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11716      * @return {BasicForm} this
11717      */
11718     setValues : function(values){
11719         if(values instanceof Array){ // array of objects
11720             for(var i = 0, len = values.length; i < len; i++){
11721                 var v = values[i];
11722                 var f = this.findField(v.id);
11723                 if(f){
11724                     f.setValue(v.value);
11725                     if(this.trackResetOnLoad){
11726                         f.originalValue = f.getValue();
11727                     }
11728                 }
11729             }
11730         }else{ // object hash
11731             var field, id;
11732             for(id in values){
11733                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11734
11735                     if (field.setFromData &&
11736                         field.valueField &&
11737                         field.displayField &&
11738                         // combos' with local stores can
11739                         // be queried via setValue()
11740                         // to set their value..
11741                         (field.store && !field.store.isLocal)
11742                         ) {
11743                         // it's a combo
11744                         var sd = { };
11745                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11746                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11747                         field.setFromData(sd);
11748
11749                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11750                         
11751                         field.setFromData(values);
11752                         
11753                     } else {
11754                         field.setValue(values[id]);
11755                     }
11756
11757
11758                     if(this.trackResetOnLoad){
11759                         field.originalValue = field.getValue();
11760                     }
11761                 }
11762             }
11763         }
11764
11765         //Roo.each(this.childForms || [], function (f) {
11766         //    f.setValues(values);
11767         //});
11768
11769         return this;
11770     },
11771
11772     /**
11773      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11774      * they are returned as an array.
11775      * @param {Boolean} asString
11776      * @return {Object}
11777      */
11778     getValues : function(asString){
11779         //if (this.childForms) {
11780             // copy values from the child forms
11781         //    Roo.each(this.childForms, function (f) {
11782         //        this.setValues(f.getValues());
11783         //    }, this);
11784         //}
11785
11786
11787
11788         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11789         if(asString === true){
11790             return fs;
11791         }
11792         return Roo.urlDecode(fs);
11793     },
11794
11795     /**
11796      * Returns the fields in this form as an object with key/value pairs.
11797      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11798      * @return {Object}
11799      */
11800     getFieldValues : function(with_hidden)
11801     {
11802         var items = this.getItems();
11803         var ret = {};
11804         items.each(function(f){
11805             
11806             if (!f.getName()) {
11807                 return;
11808             }
11809             
11810             var v = f.getValue();
11811             
11812             if (f.inputType =='radio') {
11813                 if (typeof(ret[f.getName()]) == 'undefined') {
11814                     ret[f.getName()] = ''; // empty..
11815                 }
11816
11817                 if (!f.el.dom.checked) {
11818                     return;
11819
11820                 }
11821                 v = f.el.dom.value;
11822
11823             }
11824             
11825             if(f.xtype == 'MoneyField'){
11826                 ret[f.currencyName] = f.getCurrency();
11827             }
11828
11829             // not sure if this supported any more..
11830             if ((typeof(v) == 'object') && f.getRawValue) {
11831                 v = f.getRawValue() ; // dates..
11832             }
11833             // combo boxes where name != hiddenName...
11834             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11835                 ret[f.name] = f.getRawValue();
11836             }
11837             ret[f.getName()] = v;
11838         });
11839
11840         return ret;
11841     },
11842
11843     /**
11844      * Clears all invalid messages in this form.
11845      * @return {BasicForm} this
11846      */
11847     clearInvalid : function(){
11848         var items = this.getItems();
11849
11850         items.each(function(f){
11851            f.clearInvalid();
11852         });
11853
11854         return this;
11855     },
11856
11857     /**
11858      * Resets this form.
11859      * @return {BasicForm} this
11860      */
11861     reset : function(){
11862         var items = this.getItems();
11863         items.each(function(f){
11864             f.reset();
11865         });
11866
11867         Roo.each(this.childForms || [], function (f) {
11868             f.reset();
11869         });
11870
11871
11872         return this;
11873     },
11874     
11875     getItems : function()
11876     {
11877         var r=new Roo.util.MixedCollection(false, function(o){
11878             return o.id || (o.id = Roo.id());
11879         });
11880         var iter = function(el) {
11881             if (el.inputEl) {
11882                 r.add(el);
11883             }
11884             if (!el.items) {
11885                 return;
11886             }
11887             Roo.each(el.items,function(e) {
11888                 iter(e);
11889             });
11890         };
11891
11892         iter(this);
11893         return r;
11894     },
11895     
11896     hideFields : function(items)
11897     {
11898         Roo.each(items, function(i){
11899             
11900             var f = this.findField(i);
11901             
11902             if(!f){
11903                 return;
11904             }
11905             
11906             f.hide();
11907             
11908         }, this);
11909     },
11910     
11911     showFields : function(items)
11912     {
11913         Roo.each(items, function(i){
11914             
11915             var f = this.findField(i);
11916             
11917             if(!f){
11918                 return;
11919             }
11920             
11921             f.show();
11922             
11923         }, this);
11924     }
11925
11926 });
11927
11928 Roo.apply(Roo.bootstrap.form.Form, {
11929     
11930     popover : {
11931         
11932         padding : 5,
11933         
11934         isApplied : false,
11935         
11936         isMasked : false,
11937         
11938         form : false,
11939         
11940         target : false,
11941         
11942         toolTip : false,
11943         
11944         intervalID : false,
11945         
11946         maskEl : false,
11947         
11948         apply : function()
11949         {
11950             if(this.isApplied){
11951                 return;
11952             }
11953             
11954             this.maskEl = {
11955                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11956                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11957                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11958                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11959             };
11960             
11961             this.maskEl.top.enableDisplayMode("block");
11962             this.maskEl.left.enableDisplayMode("block");
11963             this.maskEl.bottom.enableDisplayMode("block");
11964             this.maskEl.right.enableDisplayMode("block");
11965             
11966             this.toolTip = new Roo.bootstrap.Tooltip({
11967                 cls : 'roo-form-error-popover',
11968                 alignment : {
11969                     'left' : ['r-l', [-2,0], 'right'],
11970                     'right' : ['l-r', [2,0], 'left'],
11971                     'bottom' : ['tl-bl', [0,2], 'top'],
11972                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11973                 }
11974             });
11975             
11976             this.toolTip.render(Roo.get(document.body));
11977
11978             this.toolTip.el.enableDisplayMode("block");
11979             
11980             Roo.get(document.body).on('click', function(){
11981                 this.unmask();
11982             }, this);
11983             
11984             Roo.get(document.body).on('touchstart', function(){
11985                 this.unmask();
11986             }, this);
11987             
11988             this.isApplied = true
11989         },
11990         
11991         mask : function(form, target)
11992         {
11993             this.form = form;
11994             
11995             this.target = target;
11996             
11997             if(!this.form.errorMask || !target.el){
11998                 return;
11999             }
12000             
12001             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12002             
12003             Roo.log(scrollable);
12004             
12005             var ot = this.target.el.calcOffsetsTo(scrollable);
12006             
12007             var scrollTo = ot[1] - this.form.maskOffset;
12008             
12009             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12010             
12011             scrollable.scrollTo('top', scrollTo);
12012             
12013             var box = this.target.el.getBox();
12014             Roo.log(box);
12015             var zIndex = Roo.bootstrap.Modal.zIndex++;
12016
12017             
12018             this.maskEl.top.setStyle('position', 'absolute');
12019             this.maskEl.top.setStyle('z-index', zIndex);
12020             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12021             this.maskEl.top.setLeft(0);
12022             this.maskEl.top.setTop(0);
12023             this.maskEl.top.show();
12024             
12025             this.maskEl.left.setStyle('position', 'absolute');
12026             this.maskEl.left.setStyle('z-index', zIndex);
12027             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12028             this.maskEl.left.setLeft(0);
12029             this.maskEl.left.setTop(box.y - this.padding);
12030             this.maskEl.left.show();
12031
12032             this.maskEl.bottom.setStyle('position', 'absolute');
12033             this.maskEl.bottom.setStyle('z-index', zIndex);
12034             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12035             this.maskEl.bottom.setLeft(0);
12036             this.maskEl.bottom.setTop(box.bottom + this.padding);
12037             this.maskEl.bottom.show();
12038
12039             this.maskEl.right.setStyle('position', 'absolute');
12040             this.maskEl.right.setStyle('z-index', zIndex);
12041             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12042             this.maskEl.right.setLeft(box.right + this.padding);
12043             this.maskEl.right.setTop(box.y - this.padding);
12044             this.maskEl.right.show();
12045
12046             this.toolTip.bindEl = this.target.el;
12047
12048             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12049
12050             var tip = this.target.blankText;
12051
12052             if(this.target.getValue() !== '' ) {
12053                 
12054                 if (this.target.invalidText.length) {
12055                     tip = this.target.invalidText;
12056                 } else if (this.target.regexText.length){
12057                     tip = this.target.regexText;
12058                 }
12059             }
12060
12061             this.toolTip.show(tip);
12062
12063             this.intervalID = window.setInterval(function() {
12064                 Roo.bootstrap.form.Form.popover.unmask();
12065             }, 10000);
12066
12067             window.onwheel = function(){ return false;};
12068             
12069             (function(){ this.isMasked = true; }).defer(500, this);
12070             
12071         },
12072         
12073         unmask : function()
12074         {
12075             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12076                 return;
12077             }
12078             
12079             this.maskEl.top.setStyle('position', 'absolute');
12080             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12081             this.maskEl.top.hide();
12082
12083             this.maskEl.left.setStyle('position', 'absolute');
12084             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12085             this.maskEl.left.hide();
12086
12087             this.maskEl.bottom.setStyle('position', 'absolute');
12088             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12089             this.maskEl.bottom.hide();
12090
12091             this.maskEl.right.setStyle('position', 'absolute');
12092             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12093             this.maskEl.right.hide();
12094             
12095             this.toolTip.hide();
12096             
12097             this.toolTip.el.hide();
12098             
12099             window.onwheel = function(){ return true;};
12100             
12101             if(this.intervalID){
12102                 window.clearInterval(this.intervalID);
12103                 this.intervalID = false;
12104             }
12105             
12106             this.isMasked = false;
12107             
12108         }
12109         
12110     }
12111     
12112 });
12113
12114 /*
12115  * Based on:
12116  * Ext JS Library 1.1.1
12117  * Copyright(c) 2006-2007, Ext JS, LLC.
12118  *
12119  * Originally Released Under LGPL - original licence link has changed is not relivant.
12120  *
12121  * Fork - LGPL
12122  * <script type="text/javascript">
12123  */
12124 /**
12125  * @class Roo.form.VTypes
12126  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12127  * @singleton
12128  */
12129 Roo.form.VTypes = function(){
12130     // closure these in so they are only created once.
12131     var alpha = /^[a-zA-Z_]+$/;
12132     var alphanum = /^[a-zA-Z0-9_]+$/;
12133     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12134     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12135
12136     // All these messages and functions are configurable
12137     return {
12138         /**
12139          * The function used to validate email addresses
12140          * @param {String} value The email address
12141          */
12142         'email' : function(v){
12143             return email.test(v);
12144         },
12145         /**
12146          * The error text to display when the email validation function returns false
12147          * @type String
12148          */
12149         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12150         /**
12151          * The keystroke filter mask to be applied on email input
12152          * @type RegExp
12153          */
12154         'emailMask' : /[a-z0-9_\.\-@]/i,
12155
12156         /**
12157          * The function used to validate URLs
12158          * @param {String} value The URL
12159          */
12160         'url' : function(v){
12161             return url.test(v);
12162         },
12163         /**
12164          * The error text to display when the url validation function returns false
12165          * @type String
12166          */
12167         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12168         
12169         /**
12170          * The function used to validate alpha values
12171          * @param {String} value The value
12172          */
12173         'alpha' : function(v){
12174             return alpha.test(v);
12175         },
12176         /**
12177          * The error text to display when the alpha validation function returns false
12178          * @type String
12179          */
12180         'alphaText' : 'This field should only contain letters and _',
12181         /**
12182          * The keystroke filter mask to be applied on alpha input
12183          * @type RegExp
12184          */
12185         'alphaMask' : /[a-z_]/i,
12186
12187         /**
12188          * The function used to validate alphanumeric values
12189          * @param {String} value The value
12190          */
12191         'alphanum' : function(v){
12192             return alphanum.test(v);
12193         },
12194         /**
12195          * The error text to display when the alphanumeric validation function returns false
12196          * @type String
12197          */
12198         'alphanumText' : 'This field should only contain letters, numbers and _',
12199         /**
12200          * The keystroke filter mask to be applied on alphanumeric input
12201          * @type RegExp
12202          */
12203         'alphanumMask' : /[a-z0-9_]/i
12204     };
12205 }();/*
12206  * - LGPL
12207  *
12208  * Input
12209  * 
12210  */
12211
12212 /**
12213  * @class Roo.bootstrap.form.Input
12214  * @extends Roo.bootstrap.Component
12215  * Bootstrap Input class
12216  * @cfg {Boolean} disabled is it disabled
12217  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12218  * @cfg {String} name name of the input
12219  * @cfg {string} fieldLabel - the label associated
12220  * @cfg {string} placeholder - placeholder to put in text.
12221  * @cfg {string} before - input group add on before
12222  * @cfg {string} after - input group add on after
12223  * @cfg {string} size - (lg|sm) or leave empty..
12224  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12225  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12226  * @cfg {Number} md colspan out of 12 for computer-sized screens
12227  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12228  * @cfg {string} value default value of the input
12229  * @cfg {Number} labelWidth set the width of label 
12230  * @cfg {Number} labellg set the width of label (1-12)
12231  * @cfg {Number} labelmd set the width of label (1-12)
12232  * @cfg {Number} labelsm set the width of label (1-12)
12233  * @cfg {Number} labelxs set the width of label (1-12)
12234  * @cfg {String} labelAlign (top|left)
12235  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12236  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12237  * @cfg {String} indicatorpos (left|right) default left
12238  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12239  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12240  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12241  * @cfg {Roo.bootstrap.Button} before Button to show before
12242  * @cfg {Roo.bootstrap.Button} afterButton to show before
12243  * @cfg {String} align (left|center|right) Default left
12244  * @cfg {Boolean} forceFeedback (true|false) Default false
12245  * 
12246  * @constructor
12247  * Create a new Input
12248  * @param {Object} config The config object
12249  */
12250
12251 Roo.bootstrap.form.Input = function(config){
12252     
12253     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12254     
12255     this.addEvents({
12256         /**
12257          * @event focus
12258          * Fires when this field receives input focus.
12259          * @param {Roo.form.Field} this
12260          */
12261         focus : true,
12262         /**
12263          * @event blur
12264          * Fires when this field loses input focus.
12265          * @param {Roo.form.Field} this
12266          */
12267         blur : true,
12268         /**
12269          * @event specialkey
12270          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12271          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12272          * @param {Roo.form.Field} this
12273          * @param {Roo.EventObject} e The event object
12274          */
12275         specialkey : true,
12276         /**
12277          * @event change
12278          * Fires just before the field blurs if the field value has changed.
12279          * @param {Roo.form.Field} this
12280          * @param {Mixed} newValue The new value
12281          * @param {Mixed} oldValue The original value
12282          */
12283         change : true,
12284         /**
12285          * @event invalid
12286          * Fires after the field has been marked as invalid.
12287          * @param {Roo.form.Field} this
12288          * @param {String} msg The validation message
12289          */
12290         invalid : true,
12291         /**
12292          * @event valid
12293          * Fires after the field has been validated with no errors.
12294          * @param {Roo.form.Field} this
12295          */
12296         valid : true,
12297          /**
12298          * @event keyup
12299          * Fires after the key up
12300          * @param {Roo.form.Field} this
12301          * @param {Roo.EventObject}  e The event Object
12302          */
12303         keyup : true,
12304         /**
12305          * @event paste
12306          * Fires after the user pastes into input
12307          * @param {Roo.form.Field} this
12308          * @param {Roo.EventObject}  e The event Object
12309          */
12310         paste : true
12311     });
12312 };
12313
12314 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12315      /**
12316      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12317       automatic validation (defaults to "keyup").
12318      */
12319     validationEvent : "keyup",
12320      /**
12321      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12322      */
12323     validateOnBlur : true,
12324     /**
12325      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12326      */
12327     validationDelay : 250,
12328      /**
12329      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12330      */
12331     focusClass : "x-form-focus",  // not needed???
12332     
12333        
12334     /**
12335      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12336      */
12337     invalidClass : "has-warning",
12338     
12339     /**
12340      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12341      */
12342     validClass : "has-success",
12343     
12344     /**
12345      * @cfg {Boolean} hasFeedback (true|false) default true
12346      */
12347     hasFeedback : true,
12348     
12349     /**
12350      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12351      */
12352     invalidFeedbackClass : "glyphicon-warning-sign",
12353     
12354     /**
12355      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12356      */
12357     validFeedbackClass : "glyphicon-ok",
12358     
12359     /**
12360      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12361      */
12362     selectOnFocus : false,
12363     
12364      /**
12365      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12366      */
12367     maskRe : null,
12368        /**
12369      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12370      */
12371     vtype : null,
12372     
12373       /**
12374      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12375      */
12376     disableKeyFilter : false,
12377     
12378        /**
12379      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12380      */
12381     disabled : false,
12382      /**
12383      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12384      */
12385     allowBlank : true,
12386     /**
12387      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12388      */
12389     blankText : "Please complete this mandatory field",
12390     
12391      /**
12392      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12393      */
12394     minLength : 0,
12395     /**
12396      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12397      */
12398     maxLength : Number.MAX_VALUE,
12399     /**
12400      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12401      */
12402     minLengthText : "The minimum length for this field is {0}",
12403     /**
12404      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12405      */
12406     maxLengthText : "The maximum length for this field is {0}",
12407   
12408     
12409     /**
12410      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12411      * If available, this function will be called only after the basic validators all return true, and will be passed the
12412      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12413      */
12414     validator : null,
12415     /**
12416      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12417      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12418      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12419      */
12420     regex : null,
12421     /**
12422      * @cfg {String} regexText -- Depricated - use Invalid Text
12423      */
12424     regexText : "",
12425     
12426     /**
12427      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12428      */
12429     invalidText : "",
12430     
12431     
12432     
12433     autocomplete: false,
12434     
12435     
12436     fieldLabel : '',
12437     inputType : 'text',
12438     
12439     name : false,
12440     placeholder: false,
12441     before : false,
12442     after : false,
12443     size : false,
12444     hasFocus : false,
12445     preventMark: false,
12446     isFormField : true,
12447     value : '',
12448     labelWidth : 2,
12449     labelAlign : false,
12450     readOnly : false,
12451     align : false,
12452     formatedValue : false,
12453     forceFeedback : false,
12454     
12455     indicatorpos : 'left',
12456     
12457     labellg : 0,
12458     labelmd : 0,
12459     labelsm : 0,
12460     labelxs : 0,
12461     
12462     capture : '',
12463     accept : '',
12464     
12465     parentLabelAlign : function()
12466     {
12467         var parent = this;
12468         while (parent.parent()) {
12469             parent = parent.parent();
12470             if (typeof(parent.labelAlign) !='undefined') {
12471                 return parent.labelAlign;
12472             }
12473         }
12474         return 'left';
12475         
12476     },
12477     
12478     getAutoCreate : function()
12479     {
12480         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12481         
12482         var id = Roo.id();
12483         
12484         var cfg = {};
12485         
12486         if(this.inputType != 'hidden'){
12487             cfg.cls = 'form-group' //input-group
12488         }
12489         
12490         var input =  {
12491             tag: 'input',
12492             id : id,
12493             type : this.inputType,
12494             value : this.value,
12495             cls : 'form-control',
12496             placeholder : this.placeholder || '',
12497             autocomplete : this.autocomplete || 'new-password'
12498         };
12499         if (this.inputType == 'file') {
12500             input.style = 'overflow:hidden'; // why not in CSS?
12501         }
12502         
12503         if(this.capture.length){
12504             input.capture = this.capture;
12505         }
12506         
12507         if(this.accept.length){
12508             input.accept = this.accept + "/*";
12509         }
12510         
12511         if(this.align){
12512             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12513         }
12514         
12515         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12516             input.maxLength = this.maxLength;
12517         }
12518         
12519         if (this.disabled) {
12520             input.disabled=true;
12521         }
12522         
12523         if (this.readOnly) {
12524             input.readonly=true;
12525         }
12526         
12527         if (this.name) {
12528             input.name = this.name;
12529         }
12530         
12531         if (this.size) {
12532             input.cls += ' input-' + this.size;
12533         }
12534         
12535         var settings=this;
12536         ['xs','sm','md','lg'].map(function(size){
12537             if (settings[size]) {
12538                 cfg.cls += ' col-' + size + '-' + settings[size];
12539             }
12540         });
12541         
12542         var inputblock = input;
12543         
12544         var feedback = {
12545             tag: 'span',
12546             cls: 'glyphicon form-control-feedback'
12547         };
12548             
12549         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12550             
12551             inputblock = {
12552                 cls : 'has-feedback',
12553                 cn :  [
12554                     input,
12555                     feedback
12556                 ] 
12557             };  
12558         }
12559         
12560         if (this.before || this.after) {
12561             
12562             inputblock = {
12563                 cls : 'input-group',
12564                 cn :  [] 
12565             };
12566             
12567             if (this.before && typeof(this.before) == 'string') {
12568                 
12569                 inputblock.cn.push({
12570                     tag :'span',
12571                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12572                     html : this.before
12573                 });
12574             }
12575             if (this.before && typeof(this.before) == 'object') {
12576                 this.before = Roo.factory(this.before);
12577                 
12578                 inputblock.cn.push({
12579                     tag :'span',
12580                     cls : 'roo-input-before input-group-prepend   input-group-' +
12581                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12582                 });
12583             }
12584             
12585             inputblock.cn.push(input);
12586             
12587             if (this.after && typeof(this.after) == 'string') {
12588                 inputblock.cn.push({
12589                     tag :'span',
12590                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12591                     html : this.after
12592                 });
12593             }
12594             if (this.after && typeof(this.after) == 'object') {
12595                 this.after = Roo.factory(this.after);
12596                 
12597                 inputblock.cn.push({
12598                     tag :'span',
12599                     cls : 'roo-input-after input-group-append  input-group-' +
12600                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12601                 });
12602             }
12603             
12604             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12605                 inputblock.cls += ' has-feedback';
12606                 inputblock.cn.push(feedback);
12607             }
12608         };
12609         var indicator = {
12610             tag : 'i',
12611             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12612             tooltip : 'This field is required'
12613         };
12614         if (this.allowBlank ) {
12615             indicator.style = this.allowBlank ? ' display:none' : '';
12616         }
12617         if (align ==='left' && this.fieldLabel.length) {
12618             
12619             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12620             
12621             cfg.cn = [
12622                 indicator,
12623                 {
12624                     tag: 'label',
12625                     'for' :  id,
12626                     cls : 'control-label col-form-label',
12627                     html : this.fieldLabel
12628
12629                 },
12630                 {
12631                     cls : "", 
12632                     cn: [
12633                         inputblock
12634                     ]
12635                 }
12636             ];
12637             
12638             var labelCfg = cfg.cn[1];
12639             var contentCfg = cfg.cn[2];
12640             
12641             if(this.indicatorpos == 'right'){
12642                 cfg.cn = [
12643                     {
12644                         tag: 'label',
12645                         'for' :  id,
12646                         cls : 'control-label col-form-label',
12647                         cn : [
12648                             {
12649                                 tag : 'span',
12650                                 html : this.fieldLabel
12651                             },
12652                             indicator
12653                         ]
12654                     },
12655                     {
12656                         cls : "",
12657                         cn: [
12658                             inputblock
12659                         ]
12660                     }
12661
12662                 ];
12663                 
12664                 labelCfg = cfg.cn[0];
12665                 contentCfg = cfg.cn[1];
12666             
12667             }
12668             
12669             if(this.labelWidth > 12){
12670                 labelCfg.style = "width: " + this.labelWidth + 'px';
12671             }
12672             
12673             if(this.labelWidth < 13 && this.labelmd == 0){
12674                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12675             }
12676             
12677             if(this.labellg > 0){
12678                 labelCfg.cls += ' col-lg-' + this.labellg;
12679                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12680             }
12681             
12682             if(this.labelmd > 0){
12683                 labelCfg.cls += ' col-md-' + this.labelmd;
12684                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12685             }
12686             
12687             if(this.labelsm > 0){
12688                 labelCfg.cls += ' col-sm-' + this.labelsm;
12689                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12690             }
12691             
12692             if(this.labelxs > 0){
12693                 labelCfg.cls += ' col-xs-' + this.labelxs;
12694                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12695             }
12696             
12697             
12698         } else if ( this.fieldLabel.length) {
12699                 
12700             
12701             
12702             cfg.cn = [
12703                 {
12704                     tag : 'i',
12705                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12706                     tooltip : 'This field is required',
12707                     style : this.allowBlank ? ' display:none' : '' 
12708                 },
12709                 {
12710                     tag: 'label',
12711                    //cls : 'input-group-addon',
12712                     html : this.fieldLabel
12713
12714                 },
12715
12716                inputblock
12717
12718            ];
12719            
12720            if(this.indicatorpos == 'right'){
12721        
12722                 cfg.cn = [
12723                     {
12724                         tag: 'label',
12725                        //cls : 'input-group-addon',
12726                         html : this.fieldLabel
12727
12728                     },
12729                     {
12730                         tag : 'i',
12731                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12732                         tooltip : 'This field is required',
12733                         style : this.allowBlank ? ' display:none' : '' 
12734                     },
12735
12736                    inputblock
12737
12738                ];
12739
12740             }
12741
12742         } else {
12743             
12744             cfg.cn = [
12745
12746                     inputblock
12747
12748             ];
12749                 
12750                 
12751         };
12752         
12753         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12754            cfg.cls += ' navbar-form';
12755         }
12756         
12757         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12758             // on BS4 we do this only if not form 
12759             cfg.cls += ' navbar-form';
12760             cfg.tag = 'li';
12761         }
12762         
12763         return cfg;
12764         
12765     },
12766     /**
12767      * return the real input element.
12768      */
12769     inputEl: function ()
12770     {
12771         return this.el.select('input.form-control',true).first();
12772     },
12773     
12774     tooltipEl : function()
12775     {
12776         return this.inputEl();
12777     },
12778     
12779     indicatorEl : function()
12780     {
12781         if (Roo.bootstrap.version == 4) {
12782             return false; // not enabled in v4 yet.
12783         }
12784         
12785         var indicator = this.el.select('i.roo-required-indicator',true).first();
12786         
12787         if(!indicator){
12788             return false;
12789         }
12790         
12791         return indicator;
12792         
12793     },
12794     
12795     setDisabled : function(v)
12796     {
12797         var i  = this.inputEl().dom;
12798         if (!v) {
12799             i.removeAttribute('disabled');
12800             return;
12801             
12802         }
12803         i.setAttribute('disabled','true');
12804     },
12805     initEvents : function()
12806     {
12807           
12808         this.inputEl().on("keydown" , this.fireKey,  this);
12809         this.inputEl().on("focus", this.onFocus,  this);
12810         this.inputEl().on("blur", this.onBlur,  this);
12811         
12812         this.inputEl().relayEvent('keyup', this);
12813         this.inputEl().relayEvent('paste', this);
12814         
12815         this.indicator = this.indicatorEl();
12816         
12817         if(this.indicator){
12818             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12819         }
12820  
12821         // reference to original value for reset
12822         this.originalValue = this.getValue();
12823         //Roo.form.TextField.superclass.initEvents.call(this);
12824         if(this.validationEvent == 'keyup'){
12825             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12826             this.inputEl().on('keyup', this.filterValidation, this);
12827         }
12828         else if(this.validationEvent !== false){
12829             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12830         }
12831         
12832         if(this.selectOnFocus){
12833             this.on("focus", this.preFocus, this);
12834             
12835         }
12836         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12837             this.inputEl().on("keypress", this.filterKeys, this);
12838         } else {
12839             this.inputEl().relayEvent('keypress', this);
12840         }
12841        /* if(this.grow){
12842             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12843             this.el.on("click", this.autoSize,  this);
12844         }
12845         */
12846         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12847             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12848         }
12849         
12850         if (typeof(this.before) == 'object') {
12851             this.before.render(this.el.select('.roo-input-before',true).first());
12852         }
12853         if (typeof(this.after) == 'object') {
12854             this.after.render(this.el.select('.roo-input-after',true).first());
12855         }
12856         
12857         this.inputEl().on('change', this.onChange, this);
12858         
12859     },
12860     filterValidation : function(e){
12861         if(!e.isNavKeyPress()){
12862             this.validationTask.delay(this.validationDelay);
12863         }
12864     },
12865      /**
12866      * Validates the field value
12867      * @return {Boolean} True if the value is valid, else false
12868      */
12869     validate : function(){
12870         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12871         if(this.disabled || this.validateValue(this.getRawValue())){
12872             this.markValid();
12873             return true;
12874         }
12875         
12876         this.markInvalid();
12877         return false;
12878     },
12879     
12880     
12881     /**
12882      * Validates a value according to the field's validation rules and marks the field as invalid
12883      * if the validation fails
12884      * @param {Mixed} value The value to validate
12885      * @return {Boolean} True if the value is valid, else false
12886      */
12887     validateValue : function(value)
12888     {
12889         if(this.getVisibilityEl().hasClass('hidden')){
12890             return true;
12891         }
12892         
12893         if(value.length < 1)  { // if it's blank
12894             if(this.allowBlank){
12895                 return true;
12896             }
12897             return false;
12898         }
12899         
12900         if(value.length < this.minLength){
12901             return false;
12902         }
12903         if(value.length > this.maxLength){
12904             return false;
12905         }
12906         if(this.vtype){
12907             var vt = Roo.form.VTypes;
12908             if(!vt[this.vtype](value, this)){
12909                 return false;
12910             }
12911         }
12912         if(typeof this.validator == "function"){
12913             var msg = this.validator(value);
12914             if(msg !== true){
12915                 return false;
12916             }
12917             if (typeof(msg) == 'string') {
12918                 this.invalidText = msg;
12919             }
12920         }
12921         
12922         if(this.regex && !this.regex.test(value)){
12923             return false;
12924         }
12925         
12926         return true;
12927     },
12928     
12929      // private
12930     fireKey : function(e){
12931         //Roo.log('field ' + e.getKey());
12932         if(e.isNavKeyPress()){
12933             this.fireEvent("specialkey", this, e);
12934         }
12935     },
12936     focus : function (selectText){
12937         if(this.rendered){
12938             this.inputEl().focus();
12939             if(selectText === true){
12940                 this.inputEl().dom.select();
12941             }
12942         }
12943         return this;
12944     } ,
12945     
12946     onFocus : function(){
12947         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12948            // this.el.addClass(this.focusClass);
12949         }
12950         if(!this.hasFocus){
12951             this.hasFocus = true;
12952             this.startValue = this.getValue();
12953             this.fireEvent("focus", this);
12954         }
12955     },
12956     
12957     beforeBlur : Roo.emptyFn,
12958
12959     
12960     // private
12961     onBlur : function(){
12962         this.beforeBlur();
12963         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12964             //this.el.removeClass(this.focusClass);
12965         }
12966         this.hasFocus = false;
12967         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12968             this.validate();
12969         }
12970         var v = this.getValue();
12971         if(String(v) !== String(this.startValue)){
12972             this.fireEvent('change', this, v, this.startValue);
12973         }
12974         this.fireEvent("blur", this);
12975     },
12976     
12977     onChange : function(e)
12978     {
12979         var v = this.getValue();
12980         if(String(v) !== String(this.startValue)){
12981             this.fireEvent('change', this, v, this.startValue);
12982         }
12983         
12984     },
12985     
12986     /**
12987      * Resets the current field value to the originally loaded value and clears any validation messages
12988      */
12989     reset : function(){
12990         this.setValue(this.originalValue);
12991         this.validate();
12992     },
12993      /**
12994      * Returns the name of the field
12995      * @return {Mixed} name The name field
12996      */
12997     getName: function(){
12998         return this.name;
12999     },
13000      /**
13001      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13002      * @return {Mixed} value The field value
13003      */
13004     getValue : function(){
13005         
13006         var v = this.inputEl().getValue();
13007         
13008         return v;
13009     },
13010     /**
13011      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13012      * @return {Mixed} value The field value
13013      */
13014     getRawValue : function(){
13015         var v = this.inputEl().getValue();
13016         
13017         return v;
13018     },
13019     
13020     /**
13021      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13022      * @param {Mixed} value The value to set
13023      */
13024     setRawValue : function(v){
13025         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13026     },
13027     
13028     selectText : function(start, end){
13029         var v = this.getRawValue();
13030         if(v.length > 0){
13031             start = start === undefined ? 0 : start;
13032             end = end === undefined ? v.length : end;
13033             var d = this.inputEl().dom;
13034             if(d.setSelectionRange){
13035                 d.setSelectionRange(start, end);
13036             }else if(d.createTextRange){
13037                 var range = d.createTextRange();
13038                 range.moveStart("character", start);
13039                 range.moveEnd("character", v.length-end);
13040                 range.select();
13041             }
13042         }
13043     },
13044     
13045     /**
13046      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13047      * @param {Mixed} value The value to set
13048      */
13049     setValue : function(v){
13050         this.value = v;
13051         if(this.rendered){
13052             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13053             this.validate();
13054         }
13055     },
13056     
13057     /*
13058     processValue : function(value){
13059         if(this.stripCharsRe){
13060             var newValue = value.replace(this.stripCharsRe, '');
13061             if(newValue !== value){
13062                 this.setRawValue(newValue);
13063                 return newValue;
13064             }
13065         }
13066         return value;
13067     },
13068   */
13069     preFocus : function(){
13070         
13071         if(this.selectOnFocus){
13072             this.inputEl().dom.select();
13073         }
13074     },
13075     filterKeys : function(e){
13076         var k = e.getKey();
13077         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13078             return;
13079         }
13080         var c = e.getCharCode(), cc = String.fromCharCode(c);
13081         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13082             return;
13083         }
13084         if(!this.maskRe.test(cc)){
13085             e.stopEvent();
13086         }
13087     },
13088      /**
13089      * Clear any invalid styles/messages for this field
13090      */
13091     clearInvalid : function(){
13092         
13093         if(!this.el || this.preventMark){ // not rendered
13094             return;
13095         }
13096         
13097         
13098         this.el.removeClass([this.invalidClass, 'is-invalid']);
13099         
13100         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13101             
13102             var feedback = this.el.select('.form-control-feedback', true).first();
13103             
13104             if(feedback){
13105                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13106             }
13107             
13108         }
13109         
13110         if(this.indicator){
13111             this.indicator.removeClass('visible');
13112             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13113         }
13114         
13115         this.fireEvent('valid', this);
13116     },
13117     
13118      /**
13119      * Mark this field as valid
13120      */
13121     markValid : function()
13122     {
13123         if(!this.el  || this.preventMark){ // not rendered...
13124             return;
13125         }
13126         
13127         this.el.removeClass([this.invalidClass, this.validClass]);
13128         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13129
13130         var feedback = this.el.select('.form-control-feedback', true).first();
13131             
13132         if(feedback){
13133             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13134         }
13135         
13136         if(this.indicator){
13137             this.indicator.removeClass('visible');
13138             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13139         }
13140         
13141         if(this.disabled){
13142             return;
13143         }
13144         
13145            
13146         if(this.allowBlank && !this.getRawValue().length){
13147             return;
13148         }
13149         if (Roo.bootstrap.version == 3) {
13150             this.el.addClass(this.validClass);
13151         } else {
13152             this.inputEl().addClass('is-valid');
13153         }
13154
13155         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13156             
13157             var feedback = this.el.select('.form-control-feedback', true).first();
13158             
13159             if(feedback){
13160                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13161                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13162             }
13163             
13164         }
13165         
13166         this.fireEvent('valid', this);
13167     },
13168     
13169      /**
13170      * Mark this field as invalid
13171      * @param {String} msg The validation message
13172      */
13173     markInvalid : function(msg)
13174     {
13175         if(!this.el  || this.preventMark){ // not rendered
13176             return;
13177         }
13178         
13179         this.el.removeClass([this.invalidClass, this.validClass]);
13180         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13181         
13182         var feedback = this.el.select('.form-control-feedback', true).first();
13183             
13184         if(feedback){
13185             this.el.select('.form-control-feedback', true).first().removeClass(
13186                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13187         }
13188
13189         if(this.disabled){
13190             return;
13191         }
13192         
13193         if(this.allowBlank && !this.getRawValue().length){
13194             return;
13195         }
13196         
13197         if(this.indicator){
13198             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13199             this.indicator.addClass('visible');
13200         }
13201         if (Roo.bootstrap.version == 3) {
13202             this.el.addClass(this.invalidClass);
13203         } else {
13204             this.inputEl().addClass('is-invalid');
13205         }
13206         
13207         
13208         
13209         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13210             
13211             var feedback = this.el.select('.form-control-feedback', true).first();
13212             
13213             if(feedback){
13214                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13215                 
13216                 if(this.getValue().length || this.forceFeedback){
13217                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13218                 }
13219                 
13220             }
13221             
13222         }
13223         
13224         this.fireEvent('invalid', this, msg);
13225     },
13226     // private
13227     SafariOnKeyDown : function(event)
13228     {
13229         // this is a workaround for a password hang bug on chrome/ webkit.
13230         if (this.inputEl().dom.type != 'password') {
13231             return;
13232         }
13233         
13234         var isSelectAll = false;
13235         
13236         if(this.inputEl().dom.selectionEnd > 0){
13237             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13238         }
13239         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13240             event.preventDefault();
13241             this.setValue('');
13242             return;
13243         }
13244         
13245         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13246             
13247             event.preventDefault();
13248             // this is very hacky as keydown always get's upper case.
13249             //
13250             var cc = String.fromCharCode(event.getCharCode());
13251             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13252             
13253         }
13254     },
13255     adjustWidth : function(tag, w){
13256         tag = tag.toLowerCase();
13257         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13258             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13259                 if(tag == 'input'){
13260                     return w + 2;
13261                 }
13262                 if(tag == 'textarea'){
13263                     return w-2;
13264                 }
13265             }else if(Roo.isOpera){
13266                 if(tag == 'input'){
13267                     return w + 2;
13268                 }
13269                 if(tag == 'textarea'){
13270                     return w-2;
13271                 }
13272             }
13273         }
13274         return w;
13275     },
13276     
13277     setFieldLabel : function(v)
13278     {
13279         if(!this.rendered){
13280             return;
13281         }
13282         
13283         if(this.indicatorEl()){
13284             var ar = this.el.select('label > span',true);
13285             
13286             if (ar.elements.length) {
13287                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13288                 this.fieldLabel = v;
13289                 return;
13290             }
13291             
13292             var br = this.el.select('label',true);
13293             
13294             if(br.elements.length) {
13295                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13296                 this.fieldLabel = v;
13297                 return;
13298             }
13299             
13300             Roo.log('Cannot Found any of label > span || label in input');
13301             return;
13302         }
13303         
13304         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13305         this.fieldLabel = v;
13306         
13307         
13308     }
13309 });
13310
13311  
13312 /*
13313  * - LGPL
13314  *
13315  * Input
13316  * 
13317  */
13318
13319 /**
13320  * @class Roo.bootstrap.form.TextArea
13321  * @extends Roo.bootstrap.form.Input
13322  * Bootstrap TextArea class
13323  * @cfg {Number} cols Specifies the visible width of a text area
13324  * @cfg {Number} rows Specifies the visible number of lines in a text area
13325  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13326  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13327  * @cfg {string} html text
13328  * 
13329  * @constructor
13330  * Create a new TextArea
13331  * @param {Object} config The config object
13332  */
13333
13334 Roo.bootstrap.form.TextArea = function(config){
13335     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13336    
13337 };
13338
13339 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13340      
13341     cols : false,
13342     rows : 5,
13343     readOnly : false,
13344     warp : 'soft',
13345     resize : false,
13346     value: false,
13347     html: false,
13348     
13349     getAutoCreate : function(){
13350         
13351         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13352         
13353         var id = Roo.id();
13354         
13355         var cfg = {};
13356         
13357         if(this.inputType != 'hidden'){
13358             cfg.cls = 'form-group' //input-group
13359         }
13360         
13361         var input =  {
13362             tag: 'textarea',
13363             id : id,
13364             warp : this.warp,
13365             rows : this.rows,
13366             value : this.value || '',
13367             html: this.html || '',
13368             cls : 'form-control',
13369             placeholder : this.placeholder || '' 
13370             
13371         };
13372         
13373         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13374             input.maxLength = this.maxLength;
13375         }
13376         
13377         if(this.resize){
13378             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13379         }
13380         
13381         if(this.cols){
13382             input.cols = this.cols;
13383         }
13384         
13385         if (this.readOnly) {
13386             input.readonly = true;
13387         }
13388         
13389         if (this.name) {
13390             input.name = this.name;
13391         }
13392         
13393         if (this.size) {
13394             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13395         }
13396         
13397         var settings=this;
13398         ['xs','sm','md','lg'].map(function(size){
13399             if (settings[size]) {
13400                 cfg.cls += ' col-' + size + '-' + settings[size];
13401             }
13402         });
13403         
13404         var inputblock = input;
13405         
13406         if(this.hasFeedback && !this.allowBlank){
13407             
13408             var feedback = {
13409                 tag: 'span',
13410                 cls: 'glyphicon form-control-feedback'
13411             };
13412
13413             inputblock = {
13414                 cls : 'has-feedback',
13415                 cn :  [
13416                     input,
13417                     feedback
13418                 ] 
13419             };  
13420         }
13421         
13422         
13423         if (this.before || this.after) {
13424             
13425             inputblock = {
13426                 cls : 'input-group',
13427                 cn :  [] 
13428             };
13429             if (this.before) {
13430                 inputblock.cn.push({
13431                     tag :'span',
13432                     cls : 'input-group-addon',
13433                     html : this.before
13434                 });
13435             }
13436             
13437             inputblock.cn.push(input);
13438             
13439             if(this.hasFeedback && !this.allowBlank){
13440                 inputblock.cls += ' has-feedback';
13441                 inputblock.cn.push(feedback);
13442             }
13443             
13444             if (this.after) {
13445                 inputblock.cn.push({
13446                     tag :'span',
13447                     cls : 'input-group-addon',
13448                     html : this.after
13449                 });
13450             }
13451             
13452         }
13453         
13454         if (align ==='left' && this.fieldLabel.length) {
13455             cfg.cn = [
13456                 {
13457                     tag: 'label',
13458                     'for' :  id,
13459                     cls : 'control-label',
13460                     html : this.fieldLabel
13461                 },
13462                 {
13463                     cls : "",
13464                     cn: [
13465                         inputblock
13466                     ]
13467                 }
13468
13469             ];
13470             
13471             if(this.labelWidth > 12){
13472                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13473             }
13474
13475             if(this.labelWidth < 13 && this.labelmd == 0){
13476                 this.labelmd = this.labelWidth;
13477             }
13478
13479             if(this.labellg > 0){
13480                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13481                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13482             }
13483
13484             if(this.labelmd > 0){
13485                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13486                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13487             }
13488
13489             if(this.labelsm > 0){
13490                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13491                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13492             }
13493
13494             if(this.labelxs > 0){
13495                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13496                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13497             }
13498             
13499         } else if ( this.fieldLabel.length) {
13500             cfg.cn = [
13501
13502                {
13503                    tag: 'label',
13504                    //cls : 'input-group-addon',
13505                    html : this.fieldLabel
13506
13507                },
13508
13509                inputblock
13510
13511            ];
13512
13513         } else {
13514
13515             cfg.cn = [
13516
13517                 inputblock
13518
13519             ];
13520                 
13521         }
13522         
13523         if (this.disabled) {
13524             input.disabled=true;
13525         }
13526         
13527         return cfg;
13528         
13529     },
13530     /**
13531      * return the real textarea element.
13532      */
13533     inputEl: function ()
13534     {
13535         return this.el.select('textarea.form-control',true).first();
13536     },
13537     
13538     /**
13539      * Clear any invalid styles/messages for this field
13540      */
13541     clearInvalid : function()
13542     {
13543         
13544         if(!this.el || this.preventMark){ // not rendered
13545             return;
13546         }
13547         
13548         var label = this.el.select('label', true).first();
13549         var icon = this.el.select('i.fa-star', true).first();
13550         
13551         if(label && icon){
13552             icon.remove();
13553         }
13554         this.el.removeClass( this.validClass);
13555         this.inputEl().removeClass('is-invalid');
13556          
13557         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13558             
13559             var feedback = this.el.select('.form-control-feedback', true).first();
13560             
13561             if(feedback){
13562                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13563             }
13564             
13565         }
13566         
13567         this.fireEvent('valid', this);
13568     },
13569     
13570      /**
13571      * Mark this field as valid
13572      */
13573     markValid : function()
13574     {
13575         if(!this.el  || this.preventMark){ // not rendered
13576             return;
13577         }
13578         
13579         this.el.removeClass([this.invalidClass, this.validClass]);
13580         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13581         
13582         var feedback = this.el.select('.form-control-feedback', true).first();
13583             
13584         if(feedback){
13585             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13586         }
13587
13588         if(this.disabled || this.allowBlank){
13589             return;
13590         }
13591         
13592         var label = this.el.select('label', true).first();
13593         var icon = this.el.select('i.fa-star', true).first();
13594         
13595         if(label && icon){
13596             icon.remove();
13597         }
13598         if (Roo.bootstrap.version == 3) {
13599             this.el.addClass(this.validClass);
13600         } else {
13601             this.inputEl().addClass('is-valid');
13602         }
13603         
13604         
13605         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13606             
13607             var feedback = this.el.select('.form-control-feedback', true).first();
13608             
13609             if(feedback){
13610                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13611                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13612             }
13613             
13614         }
13615         
13616         this.fireEvent('valid', this);
13617     },
13618     
13619      /**
13620      * Mark this field as invalid
13621      * @param {String} msg The validation message
13622      */
13623     markInvalid : function(msg)
13624     {
13625         if(!this.el  || this.preventMark){ // not rendered
13626             return;
13627         }
13628         
13629         this.el.removeClass([this.invalidClass, this.validClass]);
13630         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13631         
13632         var feedback = this.el.select('.form-control-feedback', true).first();
13633             
13634         if(feedback){
13635             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13636         }
13637
13638         if(this.disabled || this.allowBlank){
13639             return;
13640         }
13641         
13642         var label = this.el.select('label', true).first();
13643         var icon = this.el.select('i.fa-star', true).first();
13644         
13645         if(!this.getValue().length && label && !icon){
13646             this.el.createChild({
13647                 tag : 'i',
13648                 cls : 'text-danger fa fa-lg fa-star',
13649                 tooltip : 'This field is required',
13650                 style : 'margin-right:5px;'
13651             }, label, true);
13652         }
13653         
13654         if (Roo.bootstrap.version == 3) {
13655             this.el.addClass(this.invalidClass);
13656         } else {
13657             this.inputEl().addClass('is-invalid');
13658         }
13659         
13660         // fixme ... this may be depricated need to test..
13661         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13662             
13663             var feedback = this.el.select('.form-control-feedback', true).first();
13664             
13665             if(feedback){
13666                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13667                 
13668                 if(this.getValue().length || this.forceFeedback){
13669                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13670                 }
13671                 
13672             }
13673             
13674         }
13675         
13676         this.fireEvent('invalid', this, msg);
13677     }
13678 });
13679
13680  
13681 /*
13682  * - LGPL
13683  *
13684  * trigger field - base class for combo..
13685  * 
13686  */
13687  
13688 /**
13689  * @class Roo.bootstrap.form.TriggerField
13690  * @extends Roo.bootstrap.form.Input
13691  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13692  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13693  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13694  * for which you can provide a custom implementation.  For example:
13695  * <pre><code>
13696 var trigger = new Roo.bootstrap.form.TriggerField();
13697 trigger.onTriggerClick = myTriggerFn;
13698 trigger.applyTo('my-field');
13699 </code></pre>
13700  *
13701  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13702  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13703  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13704  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13705  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13706
13707  * @constructor
13708  * Create a new TriggerField.
13709  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13710  * to the base TextField)
13711  */
13712 Roo.bootstrap.form.TriggerField = function(config){
13713     this.mimicing = false;
13714     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13715 };
13716
13717 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13718     /**
13719      * @cfg {String} triggerClass A CSS class to apply to the trigger
13720      */
13721      /**
13722      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13723      */
13724     hideTrigger:false,
13725
13726     /**
13727      * @cfg {Boolean} removable (true|false) special filter default false
13728      */
13729     removable : false,
13730     
13731     /** @cfg {Boolean} grow @hide */
13732     /** @cfg {Number} growMin @hide */
13733     /** @cfg {Number} growMax @hide */
13734
13735     /**
13736      * @hide 
13737      * @method
13738      */
13739     autoSize: Roo.emptyFn,
13740     // private
13741     monitorTab : true,
13742     // private
13743     deferHeight : true,
13744
13745     
13746     actionMode : 'wrap',
13747     
13748     caret : false,
13749     
13750     
13751     getAutoCreate : function(){
13752        
13753         var align = this.labelAlign || this.parentLabelAlign();
13754         
13755         var id = Roo.id();
13756         
13757         var cfg = {
13758             cls: 'form-group' //input-group
13759         };
13760         
13761         
13762         var input =  {
13763             tag: 'input',
13764             id : id,
13765             type : this.inputType,
13766             cls : 'form-control',
13767             autocomplete: 'new-password',
13768             placeholder : this.placeholder || '' 
13769             
13770         };
13771         if (this.name) {
13772             input.name = this.name;
13773         }
13774         if (this.size) {
13775             input.cls += ' input-' + this.size;
13776         }
13777         
13778         if (this.disabled) {
13779             input.disabled=true;
13780         }
13781         
13782         var inputblock = input;
13783         
13784         if(this.hasFeedback && !this.allowBlank){
13785             
13786             var feedback = {
13787                 tag: 'span',
13788                 cls: 'glyphicon form-control-feedback'
13789             };
13790             
13791             if(this.removable && !this.editable  ){
13792                 inputblock = {
13793                     cls : 'has-feedback',
13794                     cn :  [
13795                         inputblock,
13796                         {
13797                             tag: 'button',
13798                             html : 'x',
13799                             cls : 'roo-combo-removable-btn close'
13800                         },
13801                         feedback
13802                     ] 
13803                 };
13804             } else {
13805                 inputblock = {
13806                     cls : 'has-feedback',
13807                     cn :  [
13808                         inputblock,
13809                         feedback
13810                     ] 
13811                 };
13812             }
13813
13814         } else {
13815             if(this.removable && !this.editable ){
13816                 inputblock = {
13817                     cls : 'roo-removable',
13818                     cn :  [
13819                         inputblock,
13820                         {
13821                             tag: 'button',
13822                             html : 'x',
13823                             cls : 'roo-combo-removable-btn close'
13824                         }
13825                     ] 
13826                 };
13827             }
13828         }
13829         
13830         if (this.before || this.after) {
13831             
13832             inputblock = {
13833                 cls : 'input-group',
13834                 cn :  [] 
13835             };
13836             if (this.before) {
13837                 inputblock.cn.push({
13838                     tag :'span',
13839                     cls : 'input-group-addon input-group-prepend input-group-text',
13840                     html : this.before
13841                 });
13842             }
13843             
13844             inputblock.cn.push(input);
13845             
13846             if(this.hasFeedback && !this.allowBlank){
13847                 inputblock.cls += ' has-feedback';
13848                 inputblock.cn.push(feedback);
13849             }
13850             
13851             if (this.after) {
13852                 inputblock.cn.push({
13853                     tag :'span',
13854                     cls : 'input-group-addon input-group-append input-group-text',
13855                     html : this.after
13856                 });
13857             }
13858             
13859         };
13860         
13861       
13862         
13863         var ibwrap = inputblock;
13864         
13865         if(this.multiple){
13866             ibwrap = {
13867                 tag: 'ul',
13868                 cls: 'roo-select2-choices',
13869                 cn:[
13870                     {
13871                         tag: 'li',
13872                         cls: 'roo-select2-search-field',
13873                         cn: [
13874
13875                             inputblock
13876                         ]
13877                     }
13878                 ]
13879             };
13880                 
13881         }
13882         
13883         var combobox = {
13884             cls: 'roo-select2-container input-group',
13885             cn: [
13886                  {
13887                     tag: 'input',
13888                     type : 'hidden',
13889                     cls: 'form-hidden-field'
13890                 },
13891                 ibwrap
13892             ]
13893         };
13894         
13895         if(!this.multiple && this.showToggleBtn){
13896             
13897             var caret = {
13898                         tag: 'span',
13899                         cls: 'caret'
13900              };
13901             if (this.caret != false) {
13902                 caret = {
13903                      tag: 'i',
13904                      cls: 'fa fa-' + this.caret
13905                 };
13906                 
13907             }
13908             
13909             combobox.cn.push({
13910                 tag :'span',
13911                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13912                 cn : [
13913                     Roo.bootstrap.version == 3 ? caret : '',
13914                     {
13915                         tag: 'span',
13916                         cls: 'combobox-clear',
13917                         cn  : [
13918                             {
13919                                 tag : 'i',
13920                                 cls: 'icon-remove'
13921                             }
13922                         ]
13923                     }
13924                 ]
13925
13926             })
13927         }
13928         
13929         if(this.multiple){
13930             combobox.cls += ' roo-select2-container-multi';
13931         }
13932          var indicator = {
13933             tag : 'i',
13934             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13935             tooltip : 'This field is required'
13936         };
13937         if (Roo.bootstrap.version == 4) {
13938             indicator = {
13939                 tag : 'i',
13940                 style : 'display:none'
13941             };
13942         }
13943         
13944         
13945         if (align ==='left' && this.fieldLabel.length) {
13946             
13947             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13948
13949             cfg.cn = [
13950                 indicator,
13951                 {
13952                     tag: 'label',
13953                     'for' :  id,
13954                     cls : 'control-label',
13955                     html : this.fieldLabel
13956
13957                 },
13958                 {
13959                     cls : "", 
13960                     cn: [
13961                         combobox
13962                     ]
13963                 }
13964
13965             ];
13966             
13967             var labelCfg = cfg.cn[1];
13968             var contentCfg = cfg.cn[2];
13969             
13970             if(this.indicatorpos == 'right'){
13971                 cfg.cn = [
13972                     {
13973                         tag: 'label',
13974                         'for' :  id,
13975                         cls : 'control-label',
13976                         cn : [
13977                             {
13978                                 tag : 'span',
13979                                 html : this.fieldLabel
13980                             },
13981                             indicator
13982                         ]
13983                     },
13984                     {
13985                         cls : "", 
13986                         cn: [
13987                             combobox
13988                         ]
13989                     }
13990
13991                 ];
13992                 
13993                 labelCfg = cfg.cn[0];
13994                 contentCfg = cfg.cn[1];
13995             }
13996             
13997             if(this.labelWidth > 12){
13998                 labelCfg.style = "width: " + this.labelWidth + 'px';
13999             }
14000             
14001             if(this.labelWidth < 13 && this.labelmd == 0){
14002                 this.labelmd = this.labelWidth;
14003             }
14004             
14005             if(this.labellg > 0){
14006                 labelCfg.cls += ' col-lg-' + this.labellg;
14007                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14008             }
14009             
14010             if(this.labelmd > 0){
14011                 labelCfg.cls += ' col-md-' + this.labelmd;
14012                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14013             }
14014             
14015             if(this.labelsm > 0){
14016                 labelCfg.cls += ' col-sm-' + this.labelsm;
14017                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14018             }
14019             
14020             if(this.labelxs > 0){
14021                 labelCfg.cls += ' col-xs-' + this.labelxs;
14022                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14023             }
14024             
14025         } else if ( this.fieldLabel.length) {
14026 //                Roo.log(" label");
14027             cfg.cn = [
14028                 indicator,
14029                {
14030                    tag: 'label',
14031                    //cls : 'input-group-addon',
14032                    html : this.fieldLabel
14033
14034                },
14035
14036                combobox
14037
14038             ];
14039             
14040             if(this.indicatorpos == 'right'){
14041                 
14042                 cfg.cn = [
14043                     {
14044                        tag: 'label',
14045                        cn : [
14046                            {
14047                                tag : 'span',
14048                                html : this.fieldLabel
14049                            },
14050                            indicator
14051                        ]
14052
14053                     },
14054                     combobox
14055
14056                 ];
14057
14058             }
14059
14060         } else {
14061             
14062 //                Roo.log(" no label && no align");
14063                 cfg = combobox
14064                      
14065                 
14066         }
14067         
14068         var settings=this;
14069         ['xs','sm','md','lg'].map(function(size){
14070             if (settings[size]) {
14071                 cfg.cls += ' col-' + size + '-' + settings[size];
14072             }
14073         });
14074         
14075         return cfg;
14076         
14077     },
14078     
14079     
14080     
14081     // private
14082     onResize : function(w, h){
14083 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14084 //        if(typeof w == 'number'){
14085 //            var x = w - this.trigger.getWidth();
14086 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14087 //            this.trigger.setStyle('left', x+'px');
14088 //        }
14089     },
14090
14091     // private
14092     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14093
14094     // private
14095     getResizeEl : function(){
14096         return this.inputEl();
14097     },
14098
14099     // private
14100     getPositionEl : function(){
14101         return this.inputEl();
14102     },
14103
14104     // private
14105     alignErrorIcon : function(){
14106         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14107     },
14108
14109     // private
14110     initEvents : function(){
14111         
14112         this.createList();
14113         
14114         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14115         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14116         if(!this.multiple && this.showToggleBtn){
14117             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14118             if(this.hideTrigger){
14119                 this.trigger.setDisplayed(false);
14120             }
14121             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14122         }
14123         
14124         if(this.multiple){
14125             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14126         }
14127         
14128         if(this.removable && !this.editable && !this.tickable){
14129             var close = this.closeTriggerEl();
14130             
14131             if(close){
14132                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14133                 close.on('click', this.removeBtnClick, this, close);
14134             }
14135         }
14136         
14137         //this.trigger.addClassOnOver('x-form-trigger-over');
14138         //this.trigger.addClassOnClick('x-form-trigger-click');
14139         
14140         //if(!this.width){
14141         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14142         //}
14143     },
14144     
14145     closeTriggerEl : function()
14146     {
14147         var close = this.el.select('.roo-combo-removable-btn', true).first();
14148         return close ? close : false;
14149     },
14150     
14151     removeBtnClick : function(e, h, el)
14152     {
14153         e.preventDefault();
14154         
14155         if(this.fireEvent("remove", this) !== false){
14156             this.reset();
14157             this.fireEvent("afterremove", this)
14158         }
14159     },
14160     
14161     createList : function()
14162     {
14163         this.list = Roo.get(document.body).createChild({
14164             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14165             cls: 'typeahead typeahead-long dropdown-menu shadow',
14166             style: 'display:none'
14167         });
14168         
14169         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14170         
14171     },
14172
14173     // private
14174     initTrigger : function(){
14175        
14176     },
14177
14178     // private
14179     onDestroy : function(){
14180         if(this.trigger){
14181             this.trigger.removeAllListeners();
14182           //  this.trigger.remove();
14183         }
14184         //if(this.wrap){
14185         //    this.wrap.remove();
14186         //}
14187         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14188     },
14189
14190     // private
14191     onFocus : function(){
14192         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14193         /*
14194         if(!this.mimicing){
14195             this.wrap.addClass('x-trigger-wrap-focus');
14196             this.mimicing = true;
14197             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14198             if(this.monitorTab){
14199                 this.el.on("keydown", this.checkTab, this);
14200             }
14201         }
14202         */
14203     },
14204
14205     // private
14206     checkTab : function(e){
14207         if(e.getKey() == e.TAB){
14208             this.triggerBlur();
14209         }
14210     },
14211
14212     // private
14213     onBlur : function(){
14214         // do nothing
14215     },
14216
14217     // private
14218     mimicBlur : function(e, t){
14219         /*
14220         if(!this.wrap.contains(t) && this.validateBlur()){
14221             this.triggerBlur();
14222         }
14223         */
14224     },
14225
14226     // private
14227     triggerBlur : function(){
14228         this.mimicing = false;
14229         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14230         if(this.monitorTab){
14231             this.el.un("keydown", this.checkTab, this);
14232         }
14233         //this.wrap.removeClass('x-trigger-wrap-focus');
14234         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14235     },
14236
14237     // private
14238     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14239     validateBlur : function(e, t){
14240         return true;
14241     },
14242
14243     // private
14244     onDisable : function(){
14245         this.inputEl().dom.disabled = true;
14246         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14247         //if(this.wrap){
14248         //    this.wrap.addClass('x-item-disabled');
14249         //}
14250     },
14251
14252     // private
14253     onEnable : function(){
14254         this.inputEl().dom.disabled = false;
14255         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14256         //if(this.wrap){
14257         //    this.el.removeClass('x-item-disabled');
14258         //}
14259     },
14260
14261     // private
14262     onShow : function(){
14263         var ae = this.getActionEl();
14264         
14265         if(ae){
14266             ae.dom.style.display = '';
14267             ae.dom.style.visibility = 'visible';
14268         }
14269     },
14270
14271     // private
14272     
14273     onHide : function(){
14274         var ae = this.getActionEl();
14275         ae.dom.style.display = 'none';
14276     },
14277
14278     /**
14279      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14280      * by an implementing function.
14281      * @method
14282      * @param {EventObject} e
14283      */
14284     onTriggerClick : Roo.emptyFn
14285 });
14286  
14287 /*
14288 * Licence: LGPL
14289 */
14290
14291 /**
14292  * @class Roo.bootstrap.form.CardUploader
14293  * @extends Roo.bootstrap.Button
14294  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14295  * @cfg {Number} errorTimeout default 3000
14296  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14297  * @cfg {Array}  html The button text.
14298
14299  *
14300  * @constructor
14301  * Create a new CardUploader
14302  * @param {Object} config The config object
14303  */
14304
14305 Roo.bootstrap.form.CardUploader = function(config){
14306     
14307  
14308     
14309     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14310     
14311     
14312     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14313         return r.data.id
14314      });
14315     
14316      this.addEvents({
14317          // raw events
14318         /**
14319          * @event preview
14320          * When a image is clicked on - and needs to display a slideshow or similar..
14321          * @param {Roo.bootstrap.Card} this
14322          * @param {Object} The image information data 
14323          *
14324          */
14325         'preview' : true,
14326          /**
14327          * @event download
14328          * When a the download link is clicked
14329          * @param {Roo.bootstrap.Card} this
14330          * @param {Object} The image information data  contains 
14331          */
14332         'download' : true
14333         
14334     });
14335 };
14336  
14337 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14338     
14339      
14340     errorTimeout : 3000,
14341      
14342     images : false,
14343    
14344     fileCollection : false,
14345     allowBlank : true,
14346     
14347     getAutoCreate : function()
14348     {
14349         
14350         var cfg =  {
14351             cls :'form-group' ,
14352             cn : [
14353                
14354                 {
14355                     tag: 'label',
14356                    //cls : 'input-group-addon',
14357                     html : this.fieldLabel
14358
14359                 },
14360
14361                 {
14362                     tag: 'input',
14363                     type : 'hidden',
14364                     name : this.name,
14365                     value : this.value,
14366                     cls : 'd-none  form-control'
14367                 },
14368                 
14369                 {
14370                     tag: 'input',
14371                     multiple : 'multiple',
14372                     type : 'file',
14373                     cls : 'd-none  roo-card-upload-selector'
14374                 },
14375                 
14376                 {
14377                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14378                 },
14379                 {
14380                     cls : 'card-columns roo-card-uploader-container'
14381                 }
14382
14383             ]
14384         };
14385            
14386          
14387         return cfg;
14388     },
14389     
14390     getChildContainer : function() /// what children are added to.
14391     {
14392         return this.containerEl;
14393     },
14394    
14395     getButtonContainer : function() /// what children are added to.
14396     {
14397         return this.el.select(".roo-card-uploader-button-container").first();
14398     },
14399    
14400     initEvents : function()
14401     {
14402         
14403         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14404         
14405         var t = this;
14406         this.addxtype({
14407             xns: Roo.bootstrap,
14408
14409             xtype : 'Button',
14410             container_method : 'getButtonContainer' ,            
14411             html :  this.html, // fix changable?
14412             cls : 'w-100 ',
14413             listeners : {
14414                 'click' : function(btn, e) {
14415                     t.onClick(e);
14416                 }
14417             }
14418         });
14419         
14420         
14421         
14422         
14423         this.urlAPI = (window.createObjectURL && window) || 
14424                                 (window.URL && URL.revokeObjectURL && URL) || 
14425                                 (window.webkitURL && webkitURL);
14426                         
14427          
14428          
14429          
14430         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14431         
14432         this.selectorEl.on('change', this.onFileSelected, this);
14433         if (this.images) {
14434             var t = this;
14435             this.images.forEach(function(img) {
14436                 t.addCard(img)
14437             });
14438             this.images = false;
14439         }
14440         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14441          
14442        
14443     },
14444     
14445    
14446     onClick : function(e)
14447     {
14448         e.preventDefault();
14449          
14450         this.selectorEl.dom.click();
14451          
14452     },
14453     
14454     onFileSelected : function(e)
14455     {
14456         e.preventDefault();
14457         
14458         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14459             return;
14460         }
14461         
14462         Roo.each(this.selectorEl.dom.files, function(file){    
14463             this.addFile(file);
14464         }, this);
14465          
14466     },
14467     
14468       
14469     
14470       
14471     
14472     addFile : function(file)
14473     {
14474            
14475         if(typeof(file) === 'string'){
14476             throw "Add file by name?"; // should not happen
14477             return;
14478         }
14479         
14480         if(!file || !this.urlAPI){
14481             return;
14482         }
14483         
14484         // file;
14485         // file.type;
14486         
14487         var _this = this;
14488         
14489         
14490         var url = _this.urlAPI.createObjectURL( file);
14491            
14492         this.addCard({
14493             id : Roo.bootstrap.form.CardUploader.ID--,
14494             is_uploaded : false,
14495             src : url,
14496             srcfile : file,
14497             title : file.name,
14498             mimetype : file.type,
14499             preview : false,
14500             is_deleted : 0
14501         });
14502         
14503     },
14504     
14505     /**
14506      * addCard - add an Attachment to the uploader
14507      * @param data - the data about the image to upload
14508      *
14509      * {
14510           id : 123
14511           title : "Title of file",
14512           is_uploaded : false,
14513           src : "http://.....",
14514           srcfile : { the File upload object },
14515           mimetype : file.type,
14516           preview : false,
14517           is_deleted : 0
14518           .. any other data...
14519         }
14520      *
14521      * 
14522     */
14523     
14524     addCard : function (data)
14525     {
14526         // hidden input element?
14527         // if the file is not an image...
14528         //then we need to use something other that and header_image
14529         var t = this;
14530         //   remove.....
14531         var footer = [
14532             {
14533                 xns : Roo.bootstrap,
14534                 xtype : 'CardFooter',
14535                  items: [
14536                     {
14537                         xns : Roo.bootstrap,
14538                         xtype : 'Element',
14539                         cls : 'd-flex',
14540                         items : [
14541                             
14542                             {
14543                                 xns : Roo.bootstrap,
14544                                 xtype : 'Button',
14545                                 html : String.format("<small>{0}</small>", data.title),
14546                                 cls : 'col-10 text-left',
14547                                 size: 'sm',
14548                                 weight: 'link',
14549                                 fa : 'download',
14550                                 listeners : {
14551                                     click : function() {
14552                                      
14553                                         t.fireEvent( "download", t, data );
14554                                     }
14555                                 }
14556                             },
14557                           
14558                             {
14559                                 xns : Roo.bootstrap,
14560                                 xtype : 'Button',
14561                                 style: 'max-height: 28px; ',
14562                                 size : 'sm',
14563                                 weight: 'danger',
14564                                 cls : 'col-2',
14565                                 fa : 'times',
14566                                 listeners : {
14567                                     click : function() {
14568                                         t.removeCard(data.id)
14569                                     }
14570                                 }
14571                             }
14572                         ]
14573                     }
14574                     
14575                 ] 
14576             }
14577             
14578         ];
14579         
14580         var cn = this.addxtype(
14581             {
14582                  
14583                 xns : Roo.bootstrap,
14584                 xtype : 'Card',
14585                 closeable : true,
14586                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14587                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14588                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14589                 data : data,
14590                 html : false,
14591                  
14592                 items : footer,
14593                 initEvents : function() {
14594                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14595                     var card = this;
14596                     this.imgEl = this.el.select('.card-img-top').first();
14597                     if (this.imgEl) {
14598                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14599                         this.imgEl.set({ 'pointer' : 'cursor' });
14600                                   
14601                     }
14602                     this.getCardFooter().addClass('p-1');
14603                     
14604                   
14605                 }
14606                 
14607             }
14608         );
14609         // dont' really need ot update items.
14610         // this.items.push(cn);
14611         this.fileCollection.add(cn);
14612         
14613         if (!data.srcfile) {
14614             this.updateInput();
14615             return;
14616         }
14617             
14618         var _t = this;
14619         var reader = new FileReader();
14620         reader.addEventListener("load", function() {  
14621             data.srcdata =  reader.result;
14622             _t.updateInput();
14623         });
14624         reader.readAsDataURL(data.srcfile);
14625         
14626         
14627         
14628     },
14629     removeCard : function(id)
14630     {
14631         
14632         var card  = this.fileCollection.get(id);
14633         card.data.is_deleted = 1;
14634         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14635         //this.fileCollection.remove(card);
14636         //this.items = this.items.filter(function(e) { return e != card });
14637         // dont' really need ot update items.
14638         card.el.dom.parentNode.removeChild(card.el.dom);
14639         this.updateInput();
14640
14641         
14642     },
14643     reset: function()
14644     {
14645         this.fileCollection.each(function(card) {
14646             if (card.el.dom && card.el.dom.parentNode) {
14647                 card.el.dom.parentNode.removeChild(card.el.dom);
14648             }
14649         });
14650         this.fileCollection.clear();
14651         this.updateInput();
14652     },
14653     
14654     updateInput : function()
14655     {
14656          var data = [];
14657         this.fileCollection.each(function(e) {
14658             data.push(e.data);
14659             
14660         });
14661         this.inputEl().dom.value = JSON.stringify(data);
14662         
14663         
14664         
14665     }
14666     
14667     
14668 });
14669
14670
14671 Roo.bootstrap.form.CardUploader.ID = -1;/*
14672  * Based on:
14673  * Ext JS Library 1.1.1
14674  * Copyright(c) 2006-2007, Ext JS, LLC.
14675  *
14676  * Originally Released Under LGPL - original licence link has changed is not relivant.
14677  *
14678  * Fork - LGPL
14679  * <script type="text/javascript">
14680  */
14681
14682
14683 /**
14684  * @class Roo.data.SortTypes
14685  * @singleton
14686  * Defines the default sorting (casting?) comparison functions used when sorting data.
14687  */
14688 Roo.data.SortTypes = {
14689     /**
14690      * Default sort that does nothing
14691      * @param {Mixed} s The value being converted
14692      * @return {Mixed} The comparison value
14693      */
14694     none : function(s){
14695         return s;
14696     },
14697     
14698     /**
14699      * The regular expression used to strip tags
14700      * @type {RegExp}
14701      * @property
14702      */
14703     stripTagsRE : /<\/?[^>]+>/gi,
14704     
14705     /**
14706      * Strips all HTML tags to sort on text only
14707      * @param {Mixed} s The value being converted
14708      * @return {String} The comparison value
14709      */
14710     asText : function(s){
14711         return String(s).replace(this.stripTagsRE, "");
14712     },
14713     
14714     /**
14715      * Strips all HTML tags to sort on text only - Case insensitive
14716      * @param {Mixed} s The value being converted
14717      * @return {String} The comparison value
14718      */
14719     asUCText : function(s){
14720         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14721     },
14722     
14723     /**
14724      * Case insensitive string
14725      * @param {Mixed} s The value being converted
14726      * @return {String} The comparison value
14727      */
14728     asUCString : function(s) {
14729         return String(s).toUpperCase();
14730     },
14731     
14732     /**
14733      * Date sorting
14734      * @param {Mixed} s The value being converted
14735      * @return {Number} The comparison value
14736      */
14737     asDate : function(s) {
14738         if(!s){
14739             return 0;
14740         }
14741         if(s instanceof Date){
14742             return s.getTime();
14743         }
14744         return Date.parse(String(s));
14745     },
14746     
14747     /**
14748      * Float sorting
14749      * @param {Mixed} s The value being converted
14750      * @return {Float} The comparison value
14751      */
14752     asFloat : function(s) {
14753         var val = parseFloat(String(s).replace(/,/g, ""));
14754         if(isNaN(val)) {
14755             val = 0;
14756         }
14757         return val;
14758     },
14759     
14760     /**
14761      * Integer sorting
14762      * @param {Mixed} s The value being converted
14763      * @return {Number} The comparison value
14764      */
14765     asInt : function(s) {
14766         var val = parseInt(String(s).replace(/,/g, ""));
14767         if(isNaN(val)) {
14768             val = 0;
14769         }
14770         return val;
14771     }
14772 };/*
14773  * Based on:
14774  * Ext JS Library 1.1.1
14775  * Copyright(c) 2006-2007, Ext JS, LLC.
14776  *
14777  * Originally Released Under LGPL - original licence link has changed is not relivant.
14778  *
14779  * Fork - LGPL
14780  * <script type="text/javascript">
14781  */
14782
14783 /**
14784 * @class Roo.data.Record
14785  * Instances of this class encapsulate both record <em>definition</em> information, and record
14786  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14787  * to access Records cached in an {@link Roo.data.Store} object.<br>
14788  * <p>
14789  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14790  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14791  * objects.<br>
14792  * <p>
14793  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14794  * @constructor
14795  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14796  * {@link #create}. The parameters are the same.
14797  * @param {Array} data An associative Array of data values keyed by the field name.
14798  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14799  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14800  * not specified an integer id is generated.
14801  */
14802 Roo.data.Record = function(data, id){
14803     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14804     this.data = data;
14805 };
14806
14807 /**
14808  * Generate a constructor for a specific record layout.
14809  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14810  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14811  * Each field definition object may contain the following properties: <ul>
14812  * <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,
14813  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14814  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14815  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14816  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14817  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14818  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14819  * this may be omitted.</p></li>
14820  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14821  * <ul><li>auto (Default, implies no conversion)</li>
14822  * <li>string</li>
14823  * <li>int</li>
14824  * <li>float</li>
14825  * <li>boolean</li>
14826  * <li>date</li></ul></p></li>
14827  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14828  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14829  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14830  * by the Reader into an object that will be stored in the Record. It is passed the
14831  * following parameters:<ul>
14832  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14833  * </ul></p></li>
14834  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14835  * </ul>
14836  * <br>usage:<br><pre><code>
14837 var TopicRecord = Roo.data.Record.create(
14838     {name: 'title', mapping: 'topic_title'},
14839     {name: 'author', mapping: 'username'},
14840     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14841     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14842     {name: 'lastPoster', mapping: 'user2'},
14843     {name: 'excerpt', mapping: 'post_text'}
14844 );
14845
14846 var myNewRecord = new TopicRecord({
14847     title: 'Do my job please',
14848     author: 'noobie',
14849     totalPosts: 1,
14850     lastPost: new Date(),
14851     lastPoster: 'Animal',
14852     excerpt: 'No way dude!'
14853 });
14854 myStore.add(myNewRecord);
14855 </code></pre>
14856  * @method create
14857  * @static
14858  */
14859 Roo.data.Record.create = function(o){
14860     var f = function(){
14861         f.superclass.constructor.apply(this, arguments);
14862     };
14863     Roo.extend(f, Roo.data.Record);
14864     var p = f.prototype;
14865     p.fields = new Roo.util.MixedCollection(false, function(field){
14866         return field.name;
14867     });
14868     for(var i = 0, len = o.length; i < len; i++){
14869         p.fields.add(new Roo.data.Field(o[i]));
14870     }
14871     f.getField = function(name){
14872         return p.fields.get(name);  
14873     };
14874     return f;
14875 };
14876
14877 Roo.data.Record.AUTO_ID = 1000;
14878 Roo.data.Record.EDIT = 'edit';
14879 Roo.data.Record.REJECT = 'reject';
14880 Roo.data.Record.COMMIT = 'commit';
14881
14882 Roo.data.Record.prototype = {
14883     /**
14884      * Readonly flag - true if this record has been modified.
14885      * @type Boolean
14886      */
14887     dirty : false,
14888     editing : false,
14889     error: null,
14890     modified: null,
14891
14892     // private
14893     join : function(store){
14894         this.store = store;
14895     },
14896
14897     /**
14898      * Set the named field to the specified value.
14899      * @param {String} name The name of the field to set.
14900      * @param {Object} value The value to set the field to.
14901      */
14902     set : function(name, value){
14903         if(this.data[name] == value){
14904             return;
14905         }
14906         this.dirty = true;
14907         if(!this.modified){
14908             this.modified = {};
14909         }
14910         if(typeof this.modified[name] == 'undefined'){
14911             this.modified[name] = this.data[name];
14912         }
14913         this.data[name] = value;
14914         if(!this.editing && this.store){
14915             this.store.afterEdit(this);
14916         }       
14917     },
14918
14919     /**
14920      * Get the value of the named field.
14921      * @param {String} name The name of the field to get the value of.
14922      * @return {Object} The value of the field.
14923      */
14924     get : function(name){
14925         return this.data[name]; 
14926     },
14927
14928     // private
14929     beginEdit : function(){
14930         this.editing = true;
14931         this.modified = {}; 
14932     },
14933
14934     // private
14935     cancelEdit : function(){
14936         this.editing = false;
14937         delete this.modified;
14938     },
14939
14940     // private
14941     endEdit : function(){
14942         this.editing = false;
14943         if(this.dirty && this.store){
14944             this.store.afterEdit(this);
14945         }
14946     },
14947
14948     /**
14949      * Usually called by the {@link Roo.data.Store} which owns the Record.
14950      * Rejects all changes made to the Record since either creation, or the last commit operation.
14951      * Modified fields are reverted to their original values.
14952      * <p>
14953      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14954      * of reject operations.
14955      */
14956     reject : function(){
14957         var m = this.modified;
14958         for(var n in m){
14959             if(typeof m[n] != "function"){
14960                 this.data[n] = m[n];
14961             }
14962         }
14963         this.dirty = false;
14964         delete this.modified;
14965         this.editing = false;
14966         if(this.store){
14967             this.store.afterReject(this);
14968         }
14969     },
14970
14971     /**
14972      * Usually called by the {@link Roo.data.Store} which owns the Record.
14973      * Commits all changes made to the Record since either creation, or the last commit operation.
14974      * <p>
14975      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14976      * of commit operations.
14977      */
14978     commit : function(){
14979         this.dirty = false;
14980         delete this.modified;
14981         this.editing = false;
14982         if(this.store){
14983             this.store.afterCommit(this);
14984         }
14985     },
14986
14987     // private
14988     hasError : function(){
14989         return this.error != null;
14990     },
14991
14992     // private
14993     clearError : function(){
14994         this.error = null;
14995     },
14996
14997     /**
14998      * Creates a copy of this record.
14999      * @param {String} id (optional) A new record id if you don't want to use this record's id
15000      * @return {Record}
15001      */
15002     copy : function(newId) {
15003         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15004     }
15005 };/*
15006  * Based on:
15007  * Ext JS Library 1.1.1
15008  * Copyright(c) 2006-2007, Ext JS, LLC.
15009  *
15010  * Originally Released Under LGPL - original licence link has changed is not relivant.
15011  *
15012  * Fork - LGPL
15013  * <script type="text/javascript">
15014  */
15015
15016
15017
15018 /**
15019  * @class Roo.data.Store
15020  * @extends Roo.util.Observable
15021  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15022  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15023  * <p>
15024  * 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
15025  * has no knowledge of the format of the data returned by the Proxy.<br>
15026  * <p>
15027  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15028  * instances from the data object. These records are cached and made available through accessor functions.
15029  * @constructor
15030  * Creates a new Store.
15031  * @param {Object} config A config object containing the objects needed for the Store to access data,
15032  * and read the data into Records.
15033  */
15034 Roo.data.Store = function(config){
15035     this.data = new Roo.util.MixedCollection(false);
15036     this.data.getKey = function(o){
15037         return o.id;
15038     };
15039     this.baseParams = {};
15040     // private
15041     this.paramNames = {
15042         "start" : "start",
15043         "limit" : "limit",
15044         "sort" : "sort",
15045         "dir" : "dir",
15046         "multisort" : "_multisort"
15047     };
15048
15049     if(config && config.data){
15050         this.inlineData = config.data;
15051         delete config.data;
15052     }
15053
15054     Roo.apply(this, config);
15055     
15056     if(this.reader){ // reader passed
15057         this.reader = Roo.factory(this.reader, Roo.data);
15058         this.reader.xmodule = this.xmodule || false;
15059         if(!this.recordType){
15060             this.recordType = this.reader.recordType;
15061         }
15062         if(this.reader.onMetaChange){
15063             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15064         }
15065     }
15066
15067     if(this.recordType){
15068         this.fields = this.recordType.prototype.fields;
15069     }
15070     this.modified = [];
15071
15072     this.addEvents({
15073         /**
15074          * @event datachanged
15075          * Fires when the data cache has changed, and a widget which is using this Store
15076          * as a Record cache should refresh its view.
15077          * @param {Store} this
15078          */
15079         datachanged : true,
15080         /**
15081          * @event metachange
15082          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15083          * @param {Store} this
15084          * @param {Object} meta The JSON metadata
15085          */
15086         metachange : true,
15087         /**
15088          * @event add
15089          * Fires when Records have been added to the Store
15090          * @param {Store} this
15091          * @param {Roo.data.Record[]} records The array of Records added
15092          * @param {Number} index The index at which the record(s) were added
15093          */
15094         add : true,
15095         /**
15096          * @event remove
15097          * Fires when a Record has been removed from the Store
15098          * @param {Store} this
15099          * @param {Roo.data.Record} record The Record that was removed
15100          * @param {Number} index The index at which the record was removed
15101          */
15102         remove : true,
15103         /**
15104          * @event update
15105          * Fires when a Record has been updated
15106          * @param {Store} this
15107          * @param {Roo.data.Record} record The Record that was updated
15108          * @param {String} operation The update operation being performed.  Value may be one of:
15109          * <pre><code>
15110  Roo.data.Record.EDIT
15111  Roo.data.Record.REJECT
15112  Roo.data.Record.COMMIT
15113          * </code></pre>
15114          */
15115         update : true,
15116         /**
15117          * @event clear
15118          * Fires when the data cache has been cleared.
15119          * @param {Store} this
15120          */
15121         clear : true,
15122         /**
15123          * @event beforeload
15124          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15125          * the load action will be canceled.
15126          * @param {Store} this
15127          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15128          */
15129         beforeload : true,
15130         /**
15131          * @event beforeloadadd
15132          * Fires after a new set of Records has been loaded.
15133          * @param {Store} this
15134          * @param {Roo.data.Record[]} records The Records that were loaded
15135          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15136          */
15137         beforeloadadd : true,
15138         /**
15139          * @event load
15140          * Fires after a new set of Records has been loaded, before they are added to the store.
15141          * @param {Store} this
15142          * @param {Roo.data.Record[]} records The Records that were loaded
15143          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15144          * @params {Object} return from reader
15145          */
15146         load : true,
15147         /**
15148          * @event loadexception
15149          * Fires if an exception occurs in the Proxy during loading.
15150          * Called with the signature of the Proxy's "loadexception" event.
15151          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15152          * 
15153          * @param {Proxy} 
15154          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15155          * @param {Object} load options 
15156          * @param {Object} jsonData from your request (normally this contains the Exception)
15157          */
15158         loadexception : true
15159     });
15160     
15161     if(this.proxy){
15162         this.proxy = Roo.factory(this.proxy, Roo.data);
15163         this.proxy.xmodule = this.xmodule || false;
15164         this.relayEvents(this.proxy,  ["loadexception"]);
15165     }
15166     this.sortToggle = {};
15167     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15168
15169     Roo.data.Store.superclass.constructor.call(this);
15170
15171     if(this.inlineData){
15172         this.loadData(this.inlineData);
15173         delete this.inlineData;
15174     }
15175 };
15176
15177 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15178      /**
15179     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15180     * without a remote query - used by combo/forms at present.
15181     */
15182     
15183     /**
15184     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15185     */
15186     /**
15187     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15188     */
15189     /**
15190     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15191     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15192     */
15193     /**
15194     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15195     * on any HTTP request
15196     */
15197     /**
15198     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15199     */
15200     /**
15201     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15202     */
15203     multiSort: false,
15204     /**
15205     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15206     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15207     */
15208     remoteSort : false,
15209
15210     /**
15211     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15212      * loaded or when a record is removed. (defaults to false).
15213     */
15214     pruneModifiedRecords : false,
15215
15216     // private
15217     lastOptions : null,
15218
15219     /**
15220      * Add Records to the Store and fires the add event.
15221      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15222      */
15223     add : function(records){
15224         records = [].concat(records);
15225         for(var i = 0, len = records.length; i < len; i++){
15226             records[i].join(this);
15227         }
15228         var index = this.data.length;
15229         this.data.addAll(records);
15230         this.fireEvent("add", this, records, index);
15231     },
15232
15233     /**
15234      * Remove a Record from the Store and fires the remove event.
15235      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15236      */
15237     remove : function(record){
15238         var index = this.data.indexOf(record);
15239         this.data.removeAt(index);
15240  
15241         if(this.pruneModifiedRecords){
15242             this.modified.remove(record);
15243         }
15244         this.fireEvent("remove", this, record, index);
15245     },
15246
15247     /**
15248      * Remove all Records from the Store and fires the clear event.
15249      */
15250     removeAll : function(){
15251         this.data.clear();
15252         if(this.pruneModifiedRecords){
15253             this.modified = [];
15254         }
15255         this.fireEvent("clear", this);
15256     },
15257
15258     /**
15259      * Inserts Records to the Store at the given index and fires the add event.
15260      * @param {Number} index The start index at which to insert the passed Records.
15261      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15262      */
15263     insert : function(index, records){
15264         records = [].concat(records);
15265         for(var i = 0, len = records.length; i < len; i++){
15266             this.data.insert(index, records[i]);
15267             records[i].join(this);
15268         }
15269         this.fireEvent("add", this, records, index);
15270     },
15271
15272     /**
15273      * Get the index within the cache of the passed Record.
15274      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15275      * @return {Number} The index of the passed Record. Returns -1 if not found.
15276      */
15277     indexOf : function(record){
15278         return this.data.indexOf(record);
15279     },
15280
15281     /**
15282      * Get the index within the cache of the Record with the passed id.
15283      * @param {String} id The id of the Record to find.
15284      * @return {Number} The index of the Record. Returns -1 if not found.
15285      */
15286     indexOfId : function(id){
15287         return this.data.indexOfKey(id);
15288     },
15289
15290     /**
15291      * Get the Record with the specified id.
15292      * @param {String} id The id of the Record to find.
15293      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15294      */
15295     getById : function(id){
15296         return this.data.key(id);
15297     },
15298
15299     /**
15300      * Get the Record at the specified index.
15301      * @param {Number} index The index of the Record to find.
15302      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15303      */
15304     getAt : function(index){
15305         return this.data.itemAt(index);
15306     },
15307
15308     /**
15309      * Returns a range of Records between specified indices.
15310      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15311      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15312      * @return {Roo.data.Record[]} An array of Records
15313      */
15314     getRange : function(start, end){
15315         return this.data.getRange(start, end);
15316     },
15317
15318     // private
15319     storeOptions : function(o){
15320         o = Roo.apply({}, o);
15321         delete o.callback;
15322         delete o.scope;
15323         this.lastOptions = o;
15324     },
15325
15326     /**
15327      * Loads the Record cache from the configured Proxy using the configured Reader.
15328      * <p>
15329      * If using remote paging, then the first load call must specify the <em>start</em>
15330      * and <em>limit</em> properties in the options.params property to establish the initial
15331      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15332      * <p>
15333      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15334      * and this call will return before the new data has been loaded. Perform any post-processing
15335      * in a callback function, or in a "load" event handler.</strong>
15336      * <p>
15337      * @param {Object} options An object containing properties which control loading options:<ul>
15338      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15339      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15340      * passed the following arguments:<ul>
15341      * <li>r : Roo.data.Record[]</li>
15342      * <li>options: Options object from the load call</li>
15343      * <li>success: Boolean success indicator</li></ul></li>
15344      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15345      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15346      * </ul>
15347      */
15348     load : function(options){
15349         options = options || {};
15350         if(this.fireEvent("beforeload", this, options) !== false){
15351             this.storeOptions(options);
15352             var p = Roo.apply(options.params || {}, this.baseParams);
15353             // if meta was not loaded from remote source.. try requesting it.
15354             if (!this.reader.metaFromRemote) {
15355                 p._requestMeta = 1;
15356             }
15357             if(this.sortInfo && this.remoteSort){
15358                 var pn = this.paramNames;
15359                 p[pn["sort"]] = this.sortInfo.field;
15360                 p[pn["dir"]] = this.sortInfo.direction;
15361             }
15362             if (this.multiSort) {
15363                 var pn = this.paramNames;
15364                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15365             }
15366             
15367             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15368         }
15369     },
15370
15371     /**
15372      * Reloads the Record cache from the configured Proxy using the configured Reader and
15373      * the options from the last load operation performed.
15374      * @param {Object} options (optional) An object containing properties which may override the options
15375      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15376      * the most recently used options are reused).
15377      */
15378     reload : function(options){
15379         this.load(Roo.applyIf(options||{}, this.lastOptions));
15380     },
15381
15382     // private
15383     // Called as a callback by the Reader during a load operation.
15384     loadRecords : function(o, options, success){
15385         if(!o || success === false){
15386             if(success !== false){
15387                 this.fireEvent("load", this, [], options, o);
15388             }
15389             if(options.callback){
15390                 options.callback.call(options.scope || this, [], options, false);
15391             }
15392             return;
15393         }
15394         // if data returned failure - throw an exception.
15395         if (o.success === false) {
15396             // show a message if no listener is registered.
15397             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15398                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15399             }
15400             // loadmask wil be hooked into this..
15401             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15402             return;
15403         }
15404         var r = o.records, t = o.totalRecords || r.length;
15405         
15406         this.fireEvent("beforeloadadd", this, r, options, o);
15407         
15408         if(!options || options.add !== true){
15409             if(this.pruneModifiedRecords){
15410                 this.modified = [];
15411             }
15412             for(var i = 0, len = r.length; i < len; i++){
15413                 r[i].join(this);
15414             }
15415             if(this.snapshot){
15416                 this.data = this.snapshot;
15417                 delete this.snapshot;
15418             }
15419             this.data.clear();
15420             this.data.addAll(r);
15421             this.totalLength = t;
15422             this.applySort();
15423             this.fireEvent("datachanged", this);
15424         }else{
15425             this.totalLength = Math.max(t, this.data.length+r.length);
15426             this.add(r);
15427         }
15428         
15429         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15430                 
15431             var e = new Roo.data.Record({});
15432
15433             e.set(this.parent.displayField, this.parent.emptyTitle);
15434             e.set(this.parent.valueField, '');
15435
15436             this.insert(0, e);
15437         }
15438             
15439         this.fireEvent("load", this, r, options, o);
15440         if(options.callback){
15441             options.callback.call(options.scope || this, r, options, true);
15442         }
15443     },
15444
15445
15446     /**
15447      * Loads data from a passed data block. A Reader which understands the format of the data
15448      * must have been configured in the constructor.
15449      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15450      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15451      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15452      */
15453     loadData : function(o, append){
15454         var r = this.reader.readRecords(o);
15455         this.loadRecords(r, {add: append}, true);
15456     },
15457     
15458      /**
15459      * using 'cn' the nested child reader read the child array into it's child stores.
15460      * @param {Object} rec The record with a 'children array
15461      */
15462     loadDataFromChildren : function(rec)
15463     {
15464         this.loadData(this.reader.toLoadData(rec));
15465     },
15466     
15467
15468     /**
15469      * Gets the number of cached records.
15470      * <p>
15471      * <em>If using paging, this may not be the total size of the dataset. If the data object
15472      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15473      * the data set size</em>
15474      */
15475     getCount : function(){
15476         return this.data.length || 0;
15477     },
15478
15479     /**
15480      * Gets the total number of records in the dataset as returned by the server.
15481      * <p>
15482      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15483      * the dataset size</em>
15484      */
15485     getTotalCount : function(){
15486         return this.totalLength || 0;
15487     },
15488
15489     /**
15490      * Returns the sort state of the Store as an object with two properties:
15491      * <pre><code>
15492  field {String} The name of the field by which the Records are sorted
15493  direction {String} The sort order, "ASC" or "DESC"
15494      * </code></pre>
15495      */
15496     getSortState : function(){
15497         return this.sortInfo;
15498     },
15499
15500     // private
15501     applySort : function(){
15502         if(this.sortInfo && !this.remoteSort){
15503             var s = this.sortInfo, f = s.field;
15504             var st = this.fields.get(f).sortType;
15505             var fn = function(r1, r2){
15506                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15507                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15508             };
15509             this.data.sort(s.direction, fn);
15510             if(this.snapshot && this.snapshot != this.data){
15511                 this.snapshot.sort(s.direction, fn);
15512             }
15513         }
15514     },
15515
15516     /**
15517      * Sets the default sort column and order to be used by the next load operation.
15518      * @param {String} fieldName The name of the field to sort by.
15519      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15520      */
15521     setDefaultSort : function(field, dir){
15522         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15523     },
15524
15525     /**
15526      * Sort the Records.
15527      * If remote sorting is used, the sort is performed on the server, and the cache is
15528      * reloaded. If local sorting is used, the cache is sorted internally.
15529      * @param {String} fieldName The name of the field to sort by.
15530      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15531      */
15532     sort : function(fieldName, dir){
15533         var f = this.fields.get(fieldName);
15534         if(!dir){
15535             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15536             
15537             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15538                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15539             }else{
15540                 dir = f.sortDir;
15541             }
15542         }
15543         this.sortToggle[f.name] = dir;
15544         this.sortInfo = {field: f.name, direction: dir};
15545         if(!this.remoteSort){
15546             this.applySort();
15547             this.fireEvent("datachanged", this);
15548         }else{
15549             this.load(this.lastOptions);
15550         }
15551     },
15552
15553     /**
15554      * Calls the specified function for each of the Records in the cache.
15555      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15556      * Returning <em>false</em> aborts and exits the iteration.
15557      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15558      */
15559     each : function(fn, scope){
15560         this.data.each(fn, scope);
15561     },
15562
15563     /**
15564      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15565      * (e.g., during paging).
15566      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15567      */
15568     getModifiedRecords : function(){
15569         return this.modified;
15570     },
15571
15572     // private
15573     createFilterFn : function(property, value, anyMatch){
15574         if(!value.exec){ // not a regex
15575             value = String(value);
15576             if(value.length == 0){
15577                 return false;
15578             }
15579             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15580         }
15581         return function(r){
15582             return value.test(r.data[property]);
15583         };
15584     },
15585
15586     /**
15587      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15588      * @param {String} property A field on your records
15589      * @param {Number} start The record index to start at (defaults to 0)
15590      * @param {Number} end The last record index to include (defaults to length - 1)
15591      * @return {Number} The sum
15592      */
15593     sum : function(property, start, end){
15594         var rs = this.data.items, v = 0;
15595         start = start || 0;
15596         end = (end || end === 0) ? end : rs.length-1;
15597
15598         for(var i = start; i <= end; i++){
15599             v += (rs[i].data[property] || 0);
15600         }
15601         return v;
15602     },
15603
15604     /**
15605      * Filter the records by a specified property.
15606      * @param {String} field A field on your records
15607      * @param {String/RegExp} value Either a string that the field
15608      * should start with or a RegExp to test against the field
15609      * @param {Boolean} anyMatch True to match any part not just the beginning
15610      */
15611     filter : function(property, value, anyMatch){
15612         var fn = this.createFilterFn(property, value, anyMatch);
15613         return fn ? this.filterBy(fn) : this.clearFilter();
15614     },
15615
15616     /**
15617      * Filter by a function. The specified function will be called with each
15618      * record in this data source. If the function returns true the record is included,
15619      * otherwise it is filtered.
15620      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15621      * @param {Object} scope (optional) The scope of the function (defaults to this)
15622      */
15623     filterBy : function(fn, scope){
15624         this.snapshot = this.snapshot || this.data;
15625         this.data = this.queryBy(fn, scope||this);
15626         this.fireEvent("datachanged", this);
15627     },
15628
15629     /**
15630      * Query the records by a specified property.
15631      * @param {String} field A field on your records
15632      * @param {String/RegExp} value Either a string that the field
15633      * should start with or a RegExp to test against the field
15634      * @param {Boolean} anyMatch True to match any part not just the beginning
15635      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15636      */
15637     query : function(property, value, anyMatch){
15638         var fn = this.createFilterFn(property, value, anyMatch);
15639         return fn ? this.queryBy(fn) : this.data.clone();
15640     },
15641
15642     /**
15643      * Query by a function. The specified function will be called with each
15644      * record in this data source. If the function returns true the record is included
15645      * in the results.
15646      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15647      * @param {Object} scope (optional) The scope of the function (defaults to this)
15648       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15649      **/
15650     queryBy : function(fn, scope){
15651         var data = this.snapshot || this.data;
15652         return data.filterBy(fn, scope||this);
15653     },
15654
15655     /**
15656      * Collects unique values for a particular dataIndex from this store.
15657      * @param {String} dataIndex The property to collect
15658      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15659      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15660      * @return {Array} An array of the unique values
15661      **/
15662     collect : function(dataIndex, allowNull, bypassFilter){
15663         var d = (bypassFilter === true && this.snapshot) ?
15664                 this.snapshot.items : this.data.items;
15665         var v, sv, r = [], l = {};
15666         for(var i = 0, len = d.length; i < len; i++){
15667             v = d[i].data[dataIndex];
15668             sv = String(v);
15669             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15670                 l[sv] = true;
15671                 r[r.length] = v;
15672             }
15673         }
15674         return r;
15675     },
15676
15677     /**
15678      * Revert to a view of the Record cache with no filtering applied.
15679      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15680      */
15681     clearFilter : function(suppressEvent){
15682         if(this.snapshot && this.snapshot != this.data){
15683             this.data = this.snapshot;
15684             delete this.snapshot;
15685             if(suppressEvent !== true){
15686                 this.fireEvent("datachanged", this);
15687             }
15688         }
15689     },
15690
15691     // private
15692     afterEdit : function(record){
15693         if(this.modified.indexOf(record) == -1){
15694             this.modified.push(record);
15695         }
15696         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15697     },
15698     
15699     // private
15700     afterReject : function(record){
15701         this.modified.remove(record);
15702         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15703     },
15704
15705     // private
15706     afterCommit : function(record){
15707         this.modified.remove(record);
15708         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15709     },
15710
15711     /**
15712      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15713      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15714      */
15715     commitChanges : function(){
15716         var m = this.modified.slice(0);
15717         this.modified = [];
15718         for(var i = 0, len = m.length; i < len; i++){
15719             m[i].commit();
15720         }
15721     },
15722
15723     /**
15724      * Cancel outstanding changes on all changed records.
15725      */
15726     rejectChanges : function(){
15727         var m = this.modified.slice(0);
15728         this.modified = [];
15729         for(var i = 0, len = m.length; i < len; i++){
15730             m[i].reject();
15731         }
15732     },
15733
15734     onMetaChange : function(meta, rtype, o){
15735         this.recordType = rtype;
15736         this.fields = rtype.prototype.fields;
15737         delete this.snapshot;
15738         this.sortInfo = meta.sortInfo || this.sortInfo;
15739         this.modified = [];
15740         this.fireEvent('metachange', this, this.reader.meta);
15741     },
15742     
15743     moveIndex : function(data, type)
15744     {
15745         var index = this.indexOf(data);
15746         
15747         var newIndex = index + type;
15748         
15749         this.remove(data);
15750         
15751         this.insert(newIndex, data);
15752         
15753     }
15754 });/*
15755  * Based on:
15756  * Ext JS Library 1.1.1
15757  * Copyright(c) 2006-2007, Ext JS, LLC.
15758  *
15759  * Originally Released Under LGPL - original licence link has changed is not relivant.
15760  *
15761  * Fork - LGPL
15762  * <script type="text/javascript">
15763  */
15764
15765 /**
15766  * @class Roo.data.SimpleStore
15767  * @extends Roo.data.Store
15768  * Small helper class to make creating Stores from Array data easier.
15769  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15770  * @cfg {Array} fields An array of field definition objects, or field name strings.
15771  * @cfg {Object} an existing reader (eg. copied from another store)
15772  * @cfg {Array} data The multi-dimensional array of data
15773  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15774  * @cfg {Roo.data.Reader} reader  [not-required] 
15775  * @constructor
15776  * @param {Object} config
15777  */
15778 Roo.data.SimpleStore = function(config)
15779 {
15780     Roo.data.SimpleStore.superclass.constructor.call(this, {
15781         isLocal : true,
15782         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15783                 id: config.id
15784             },
15785             Roo.data.Record.create(config.fields)
15786         ),
15787         proxy : new Roo.data.MemoryProxy(config.data)
15788     });
15789     this.load();
15790 };
15791 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15792  * Based on:
15793  * Ext JS Library 1.1.1
15794  * Copyright(c) 2006-2007, Ext JS, LLC.
15795  *
15796  * Originally Released Under LGPL - original licence link has changed is not relivant.
15797  *
15798  * Fork - LGPL
15799  * <script type="text/javascript">
15800  */
15801
15802 /**
15803 /**
15804  * @extends Roo.data.Store
15805  * @class Roo.data.JsonStore
15806  * Small helper class to make creating Stores for JSON data easier. <br/>
15807 <pre><code>
15808 var store = new Roo.data.JsonStore({
15809     url: 'get-images.php',
15810     root: 'images',
15811     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15812 });
15813 </code></pre>
15814  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15815  * JsonReader and HttpProxy (unless inline data is provided).</b>
15816  * @cfg {Array} fields An array of field definition objects, or field name strings.
15817  * @constructor
15818  * @param {Object} config
15819  */
15820 Roo.data.JsonStore = function(c){
15821     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15822         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15823         reader: new Roo.data.JsonReader(c, c.fields)
15824     }));
15825 };
15826 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15827  * Based on:
15828  * Ext JS Library 1.1.1
15829  * Copyright(c) 2006-2007, Ext JS, LLC.
15830  *
15831  * Originally Released Under LGPL - original licence link has changed is not relivant.
15832  *
15833  * Fork - LGPL
15834  * <script type="text/javascript">
15835  */
15836
15837  
15838 Roo.data.Field = function(config){
15839     if(typeof config == "string"){
15840         config = {name: config};
15841     }
15842     Roo.apply(this, config);
15843     
15844     if(!this.type){
15845         this.type = "auto";
15846     }
15847     
15848     var st = Roo.data.SortTypes;
15849     // named sortTypes are supported, here we look them up
15850     if(typeof this.sortType == "string"){
15851         this.sortType = st[this.sortType];
15852     }
15853     
15854     // set default sortType for strings and dates
15855     if(!this.sortType){
15856         switch(this.type){
15857             case "string":
15858                 this.sortType = st.asUCString;
15859                 break;
15860             case "date":
15861                 this.sortType = st.asDate;
15862                 break;
15863             default:
15864                 this.sortType = st.none;
15865         }
15866     }
15867
15868     // define once
15869     var stripRe = /[\$,%]/g;
15870
15871     // prebuilt conversion function for this field, instead of
15872     // switching every time we're reading a value
15873     if(!this.convert){
15874         var cv, dateFormat = this.dateFormat;
15875         switch(this.type){
15876             case "":
15877             case "auto":
15878             case undefined:
15879                 cv = function(v){ return v; };
15880                 break;
15881             case "string":
15882                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15883                 break;
15884             case "int":
15885                 cv = function(v){
15886                     return v !== undefined && v !== null && v !== '' ?
15887                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15888                     };
15889                 break;
15890             case "float":
15891                 cv = function(v){
15892                     return v !== undefined && v !== null && v !== '' ?
15893                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15894                     };
15895                 break;
15896             case "bool":
15897             case "boolean":
15898                 cv = function(v){ return v === true || v === "true" || v == 1; };
15899                 break;
15900             case "date":
15901                 cv = function(v){
15902                     if(!v){
15903                         return '';
15904                     }
15905                     if(v instanceof Date){
15906                         return v;
15907                     }
15908                     if(dateFormat){
15909                         if(dateFormat == "timestamp"){
15910                             return new Date(v*1000);
15911                         }
15912                         return Date.parseDate(v, dateFormat);
15913                     }
15914                     var parsed = Date.parse(v);
15915                     return parsed ? new Date(parsed) : null;
15916                 };
15917              break;
15918             
15919         }
15920         this.convert = cv;
15921     }
15922 };
15923
15924 Roo.data.Field.prototype = {
15925     dateFormat: null,
15926     defaultValue: "",
15927     mapping: null,
15928     sortType : null,
15929     sortDir : "ASC"
15930 };/*
15931  * Based on:
15932  * Ext JS Library 1.1.1
15933  * Copyright(c) 2006-2007, Ext JS, LLC.
15934  *
15935  * Originally Released Under LGPL - original licence link has changed is not relivant.
15936  *
15937  * Fork - LGPL
15938  * <script type="text/javascript">
15939  */
15940  
15941 // Base class for reading structured data from a data source.  This class is intended to be
15942 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15943
15944 /**
15945  * @class Roo.data.DataReader
15946  * @abstract
15947  * Base class for reading structured data from a data source.  This class is intended to be
15948  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15949  */
15950
15951 Roo.data.DataReader = function(meta, recordType){
15952     
15953     this.meta = meta;
15954     
15955     this.recordType = recordType instanceof Array ? 
15956         Roo.data.Record.create(recordType) : recordType;
15957 };
15958
15959 Roo.data.DataReader.prototype = {
15960     
15961     
15962     readerType : 'Data',
15963      /**
15964      * Create an empty record
15965      * @param {Object} data (optional) - overlay some values
15966      * @return {Roo.data.Record} record created.
15967      */
15968     newRow :  function(d) {
15969         var da =  {};
15970         this.recordType.prototype.fields.each(function(c) {
15971             switch( c.type) {
15972                 case 'int' : da[c.name] = 0; break;
15973                 case 'date' : da[c.name] = new Date(); break;
15974                 case 'float' : da[c.name] = 0.0; break;
15975                 case 'boolean' : da[c.name] = false; break;
15976                 default : da[c.name] = ""; break;
15977             }
15978             
15979         });
15980         return new this.recordType(Roo.apply(da, d));
15981     }
15982     
15983     
15984 };/*
15985  * Based on:
15986  * Ext JS Library 1.1.1
15987  * Copyright(c) 2006-2007, Ext JS, LLC.
15988  *
15989  * Originally Released Under LGPL - original licence link has changed is not relivant.
15990  *
15991  * Fork - LGPL
15992  * <script type="text/javascript">
15993  */
15994
15995 /**
15996  * @class Roo.data.DataProxy
15997  * @extends Roo.util.Observable
15998  * @abstract
15999  * This class is an abstract base class for implementations which provide retrieval of
16000  * unformatted data objects.<br>
16001  * <p>
16002  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16003  * (of the appropriate type which knows how to parse the data object) to provide a block of
16004  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16005  * <p>
16006  * Custom implementations must implement the load method as described in
16007  * {@link Roo.data.HttpProxy#load}.
16008  */
16009 Roo.data.DataProxy = function(){
16010     this.addEvents({
16011         /**
16012          * @event beforeload
16013          * Fires before a network request is made to retrieve a data object.
16014          * @param {Object} This DataProxy object.
16015          * @param {Object} params The params parameter to the load function.
16016          */
16017         beforeload : true,
16018         /**
16019          * @event load
16020          * Fires before the load method's callback is called.
16021          * @param {Object} This DataProxy object.
16022          * @param {Object} o The data object.
16023          * @param {Object} arg The callback argument object passed to the load function.
16024          */
16025         load : true,
16026         /**
16027          * @event loadexception
16028          * Fires if an Exception occurs during data retrieval.
16029          * @param {Object} This DataProxy object.
16030          * @param {Object} o The data object.
16031          * @param {Object} arg The callback argument object passed to the load function.
16032          * @param {Object} e The Exception.
16033          */
16034         loadexception : true
16035     });
16036     Roo.data.DataProxy.superclass.constructor.call(this);
16037 };
16038
16039 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16040
16041     /**
16042      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16043      */
16044 /*
16045  * Based on:
16046  * Ext JS Library 1.1.1
16047  * Copyright(c) 2006-2007, Ext JS, LLC.
16048  *
16049  * Originally Released Under LGPL - original licence link has changed is not relivant.
16050  *
16051  * Fork - LGPL
16052  * <script type="text/javascript">
16053  */
16054 /**
16055  * @class Roo.data.MemoryProxy
16056  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16057  * to the Reader when its load method is called.
16058  * @constructor
16059  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16060  */
16061 Roo.data.MemoryProxy = function(data){
16062     if (data.data) {
16063         data = data.data;
16064     }
16065     Roo.data.MemoryProxy.superclass.constructor.call(this);
16066     this.data = data;
16067 };
16068
16069 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16070     
16071     /**
16072      * Load data from the requested source (in this case an in-memory
16073      * data object passed to the constructor), read the data object into
16074      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16075      * process that block using the passed callback.
16076      * @param {Object} params This parameter is not used by the MemoryProxy class.
16077      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16078      * object into a block of Roo.data.Records.
16079      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16080      * The function must be passed <ul>
16081      * <li>The Record block object</li>
16082      * <li>The "arg" argument from the load function</li>
16083      * <li>A boolean success indicator</li>
16084      * </ul>
16085      * @param {Object} scope The scope in which to call the callback
16086      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16087      */
16088     load : function(params, reader, callback, scope, arg){
16089         params = params || {};
16090         var result;
16091         try {
16092             result = reader.readRecords(params.data ? params.data :this.data);
16093         }catch(e){
16094             this.fireEvent("loadexception", this, arg, null, e);
16095             callback.call(scope, null, arg, false);
16096             return;
16097         }
16098         callback.call(scope, result, arg, true);
16099     },
16100     
16101     // private
16102     update : function(params, records){
16103         
16104     }
16105 });/*
16106  * Based on:
16107  * Ext JS Library 1.1.1
16108  * Copyright(c) 2006-2007, Ext JS, LLC.
16109  *
16110  * Originally Released Under LGPL - original licence link has changed is not relivant.
16111  *
16112  * Fork - LGPL
16113  * <script type="text/javascript">
16114  */
16115 /**
16116  * @class Roo.data.HttpProxy
16117  * @extends Roo.data.DataProxy
16118  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16119  * configured to reference a certain URL.<br><br>
16120  * <p>
16121  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16122  * from which the running page was served.<br><br>
16123  * <p>
16124  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16125  * <p>
16126  * Be aware that to enable the browser to parse an XML document, the server must set
16127  * the Content-Type header in the HTTP response to "text/xml".
16128  * @constructor
16129  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16130  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16131  * will be used to make the request.
16132  */
16133 Roo.data.HttpProxy = function(conn){
16134     Roo.data.HttpProxy.superclass.constructor.call(this);
16135     // is conn a conn config or a real conn?
16136     this.conn = conn;
16137     this.useAjax = !conn || !conn.events;
16138   
16139 };
16140
16141 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16142     // thse are take from connection...
16143     
16144     /**
16145      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16146      */
16147     /**
16148      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16149      * extra parameters to each request made by this object. (defaults to undefined)
16150      */
16151     /**
16152      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16153      *  to each request made by this object. (defaults to undefined)
16154      */
16155     /**
16156      * @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)
16157      */
16158     /**
16159      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16160      */
16161      /**
16162      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16163      * @type Boolean
16164      */
16165   
16166
16167     /**
16168      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16169      * @type Boolean
16170      */
16171     /**
16172      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16173      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16174      * a finer-grained basis than the DataProxy events.
16175      */
16176     getConnection : function(){
16177         return this.useAjax ? Roo.Ajax : this.conn;
16178     },
16179
16180     /**
16181      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16182      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16183      * process that block using the passed callback.
16184      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16185      * for the request to the remote server.
16186      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16187      * object into a block of Roo.data.Records.
16188      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16189      * The function must be passed <ul>
16190      * <li>The Record block object</li>
16191      * <li>The "arg" argument from the load function</li>
16192      * <li>A boolean success indicator</li>
16193      * </ul>
16194      * @param {Object} scope The scope in which to call the callback
16195      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16196      */
16197     load : function(params, reader, callback, scope, arg){
16198         if(this.fireEvent("beforeload", this, params) !== false){
16199             var  o = {
16200                 params : params || {},
16201                 request: {
16202                     callback : callback,
16203                     scope : scope,
16204                     arg : arg
16205                 },
16206                 reader: reader,
16207                 callback : this.loadResponse,
16208                 scope: this
16209             };
16210             if(this.useAjax){
16211                 Roo.applyIf(o, this.conn);
16212                 if(this.activeRequest){
16213                     Roo.Ajax.abort(this.activeRequest);
16214                 }
16215                 this.activeRequest = Roo.Ajax.request(o);
16216             }else{
16217                 this.conn.request(o);
16218             }
16219         }else{
16220             callback.call(scope||this, null, arg, false);
16221         }
16222     },
16223
16224     // private
16225     loadResponse : function(o, success, response){
16226         delete this.activeRequest;
16227         if(!success){
16228             this.fireEvent("loadexception", this, o, response);
16229             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16230             return;
16231         }
16232         var result;
16233         try {
16234             result = o.reader.read(response);
16235         }catch(e){
16236             this.fireEvent("loadexception", this, o, response, e);
16237             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16238             return;
16239         }
16240         
16241         this.fireEvent("load", this, o, o.request.arg);
16242         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16243     },
16244
16245     // private
16246     update : function(dataSet){
16247
16248     },
16249
16250     // private
16251     updateResponse : function(dataSet){
16252
16253     }
16254 });/*
16255  * Based on:
16256  * Ext JS Library 1.1.1
16257  * Copyright(c) 2006-2007, Ext JS, LLC.
16258  *
16259  * Originally Released Under LGPL - original licence link has changed is not relivant.
16260  *
16261  * Fork - LGPL
16262  * <script type="text/javascript">
16263  */
16264
16265 /**
16266  * @class Roo.data.ScriptTagProxy
16267  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16268  * other than the originating domain of the running page.<br><br>
16269  * <p>
16270  * <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
16271  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16272  * <p>
16273  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16274  * source code that is used as the source inside a &lt;script> tag.<br><br>
16275  * <p>
16276  * In order for the browser to process the returned data, the server must wrap the data object
16277  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16278  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16279  * depending on whether the callback name was passed:
16280  * <p>
16281  * <pre><code>
16282 boolean scriptTag = false;
16283 String cb = request.getParameter("callback");
16284 if (cb != null) {
16285     scriptTag = true;
16286     response.setContentType("text/javascript");
16287 } else {
16288     response.setContentType("application/x-json");
16289 }
16290 Writer out = response.getWriter();
16291 if (scriptTag) {
16292     out.write(cb + "(");
16293 }
16294 out.print(dataBlock.toJsonString());
16295 if (scriptTag) {
16296     out.write(");");
16297 }
16298 </pre></code>
16299  *
16300  * @constructor
16301  * @param {Object} config A configuration object.
16302  */
16303 Roo.data.ScriptTagProxy = function(config){
16304     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16305     Roo.apply(this, config);
16306     this.head = document.getElementsByTagName("head")[0];
16307 };
16308
16309 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16310
16311 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16312     /**
16313      * @cfg {String} url The URL from which to request the data object.
16314      */
16315     /**
16316      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16317      */
16318     timeout : 30000,
16319     /**
16320      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16321      * the server the name of the callback function set up by the load call to process the returned data object.
16322      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16323      * javascript output which calls this named function passing the data object as its only parameter.
16324      */
16325     callbackParam : "callback",
16326     /**
16327      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16328      * name to the request.
16329      */
16330     nocache : true,
16331
16332     /**
16333      * Load data from the configured URL, read the data object into
16334      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16335      * process that block using the passed callback.
16336      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16337      * for the request to the remote server.
16338      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16339      * object into a block of Roo.data.Records.
16340      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16341      * The function must be passed <ul>
16342      * <li>The Record block object</li>
16343      * <li>The "arg" argument from the load function</li>
16344      * <li>A boolean success indicator</li>
16345      * </ul>
16346      * @param {Object} scope The scope in which to call the callback
16347      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16348      */
16349     load : function(params, reader, callback, scope, arg){
16350         if(this.fireEvent("beforeload", this, params) !== false){
16351
16352             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16353
16354             var url = this.url;
16355             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16356             if(this.nocache){
16357                 url += "&_dc=" + (new Date().getTime());
16358             }
16359             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16360             var trans = {
16361                 id : transId,
16362                 cb : "stcCallback"+transId,
16363                 scriptId : "stcScript"+transId,
16364                 params : params,
16365                 arg : arg,
16366                 url : url,
16367                 callback : callback,
16368                 scope : scope,
16369                 reader : reader
16370             };
16371             var conn = this;
16372
16373             window[trans.cb] = function(o){
16374                 conn.handleResponse(o, trans);
16375             };
16376
16377             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16378
16379             if(this.autoAbort !== false){
16380                 this.abort();
16381             }
16382
16383             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16384
16385             var script = document.createElement("script");
16386             script.setAttribute("src", url);
16387             script.setAttribute("type", "text/javascript");
16388             script.setAttribute("id", trans.scriptId);
16389             this.head.appendChild(script);
16390
16391             this.trans = trans;
16392         }else{
16393             callback.call(scope||this, null, arg, false);
16394         }
16395     },
16396
16397     // private
16398     isLoading : function(){
16399         return this.trans ? true : false;
16400     },
16401
16402     /**
16403      * Abort the current server request.
16404      */
16405     abort : function(){
16406         if(this.isLoading()){
16407             this.destroyTrans(this.trans);
16408         }
16409     },
16410
16411     // private
16412     destroyTrans : function(trans, isLoaded){
16413         this.head.removeChild(document.getElementById(trans.scriptId));
16414         clearTimeout(trans.timeoutId);
16415         if(isLoaded){
16416             window[trans.cb] = undefined;
16417             try{
16418                 delete window[trans.cb];
16419             }catch(e){}
16420         }else{
16421             // if hasn't been loaded, wait for load to remove it to prevent script error
16422             window[trans.cb] = function(){
16423                 window[trans.cb] = undefined;
16424                 try{
16425                     delete window[trans.cb];
16426                 }catch(e){}
16427             };
16428         }
16429     },
16430
16431     // private
16432     handleResponse : function(o, trans){
16433         this.trans = false;
16434         this.destroyTrans(trans, true);
16435         var result;
16436         try {
16437             result = trans.reader.readRecords(o);
16438         }catch(e){
16439             this.fireEvent("loadexception", this, o, trans.arg, e);
16440             trans.callback.call(trans.scope||window, null, trans.arg, false);
16441             return;
16442         }
16443         this.fireEvent("load", this, o, trans.arg);
16444         trans.callback.call(trans.scope||window, result, trans.arg, true);
16445     },
16446
16447     // private
16448     handleFailure : function(trans){
16449         this.trans = false;
16450         this.destroyTrans(trans, false);
16451         this.fireEvent("loadexception", this, null, trans.arg);
16452         trans.callback.call(trans.scope||window, null, trans.arg, false);
16453     }
16454 });/*
16455  * Based on:
16456  * Ext JS Library 1.1.1
16457  * Copyright(c) 2006-2007, Ext JS, LLC.
16458  *
16459  * Originally Released Under LGPL - original licence link has changed is not relivant.
16460  *
16461  * Fork - LGPL
16462  * <script type="text/javascript">
16463  */
16464
16465 /**
16466  * @class Roo.data.JsonReader
16467  * @extends Roo.data.DataReader
16468  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16469  * based on mappings in a provided Roo.data.Record constructor.
16470  * 
16471  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16472  * in the reply previously. 
16473  * 
16474  * <p>
16475  * Example code:
16476  * <pre><code>
16477 var RecordDef = Roo.data.Record.create([
16478     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16479     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16480 ]);
16481 var myReader = new Roo.data.JsonReader({
16482     totalProperty: "results",    // The property which contains the total dataset size (optional)
16483     root: "rows",                // The property which contains an Array of row objects
16484     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16485 }, RecordDef);
16486 </code></pre>
16487  * <p>
16488  * This would consume a JSON file like this:
16489  * <pre><code>
16490 { 'results': 2, 'rows': [
16491     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16492     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16493 }
16494 </code></pre>
16495  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16496  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16497  * paged from the remote server.
16498  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16499  * @cfg {String} root name of the property which contains the Array of row objects.
16500  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16501  * @cfg {Array} fields Array of field definition objects
16502  * @constructor
16503  * Create a new JsonReader
16504  * @param {Object} meta Metadata configuration options
16505  * @param {Object} recordType Either an Array of field definition objects,
16506  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16507  */
16508 Roo.data.JsonReader = function(meta, recordType){
16509     
16510     meta = meta || {};
16511     // set some defaults:
16512     Roo.applyIf(meta, {
16513         totalProperty: 'total',
16514         successProperty : 'success',
16515         root : 'data',
16516         id : 'id'
16517     });
16518     
16519     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16520 };
16521 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16522     
16523     readerType : 'Json',
16524     
16525     /**
16526      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16527      * Used by Store query builder to append _requestMeta to params.
16528      * 
16529      */
16530     metaFromRemote : false,
16531     /**
16532      * This method is only used by a DataProxy which has retrieved data from a remote server.
16533      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16534      * @return {Object} data A data block which is used by an Roo.data.Store object as
16535      * a cache of Roo.data.Records.
16536      */
16537     read : function(response){
16538         var json = response.responseText;
16539        
16540         var o = /* eval:var:o */ eval("("+json+")");
16541         if(!o) {
16542             throw {message: "JsonReader.read: Json object not found"};
16543         }
16544         
16545         if(o.metaData){
16546             
16547             delete this.ef;
16548             this.metaFromRemote = true;
16549             this.meta = o.metaData;
16550             this.recordType = Roo.data.Record.create(o.metaData.fields);
16551             this.onMetaChange(this.meta, this.recordType, o);
16552         }
16553         return this.readRecords(o);
16554     },
16555
16556     // private function a store will implement
16557     onMetaChange : function(meta, recordType, o){
16558
16559     },
16560
16561     /**
16562          * @ignore
16563          */
16564     simpleAccess: function(obj, subsc) {
16565         return obj[subsc];
16566     },
16567
16568         /**
16569          * @ignore
16570          */
16571     getJsonAccessor: function(){
16572         var re = /[\[\.]/;
16573         return function(expr) {
16574             try {
16575                 return(re.test(expr))
16576                     ? new Function("obj", "return obj." + expr)
16577                     : function(obj){
16578                         return obj[expr];
16579                     };
16580             } catch(e){}
16581             return Roo.emptyFn;
16582         };
16583     }(),
16584
16585     /**
16586      * Create a data block containing Roo.data.Records from an XML document.
16587      * @param {Object} o An object which contains an Array of row objects in the property specified
16588      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16589      * which contains the total size of the dataset.
16590      * @return {Object} data A data block which is used by an Roo.data.Store object as
16591      * a cache of Roo.data.Records.
16592      */
16593     readRecords : function(o){
16594         /**
16595          * After any data loads, the raw JSON data is available for further custom processing.
16596          * @type Object
16597          */
16598         this.o = o;
16599         var s = this.meta, Record = this.recordType,
16600             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16601
16602 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16603         if (!this.ef) {
16604             if(s.totalProperty) {
16605                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16606                 }
16607                 if(s.successProperty) {
16608                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16609                 }
16610                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16611                 if (s.id) {
16612                         var g = this.getJsonAccessor(s.id);
16613                         this.getId = function(rec) {
16614                                 var r = g(rec);  
16615                                 return (r === undefined || r === "") ? null : r;
16616                         };
16617                 } else {
16618                         this.getId = function(){return null;};
16619                 }
16620             this.ef = [];
16621             for(var jj = 0; jj < fl; jj++){
16622                 f = fi[jj];
16623                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16624                 this.ef[jj] = this.getJsonAccessor(map);
16625             }
16626         }
16627
16628         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16629         if(s.totalProperty){
16630             var vt = parseInt(this.getTotal(o), 10);
16631             if(!isNaN(vt)){
16632                 totalRecords = vt;
16633             }
16634         }
16635         if(s.successProperty){
16636             var vs = this.getSuccess(o);
16637             if(vs === false || vs === 'false'){
16638                 success = false;
16639             }
16640         }
16641         var records = [];
16642         for(var i = 0; i < c; i++){
16643                 var n = root[i];
16644             var values = {};
16645             var id = this.getId(n);
16646             for(var j = 0; j < fl; j++){
16647                 f = fi[j];
16648             var v = this.ef[j](n);
16649             if (!f.convert) {
16650                 Roo.log('missing convert for ' + f.name);
16651                 Roo.log(f);
16652                 continue;
16653             }
16654             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16655             }
16656             var record = new Record(values, id);
16657             record.json = n;
16658             records[i] = record;
16659         }
16660         return {
16661             raw : o,
16662             success : success,
16663             records : records,
16664             totalRecords : totalRecords
16665         };
16666     },
16667     // used when loading children.. @see loadDataFromChildren
16668     toLoadData: function(rec)
16669     {
16670         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16671         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16672         return { data : data, total : data.length };
16673         
16674     }
16675 });/*
16676  * Based on:
16677  * Ext JS Library 1.1.1
16678  * Copyright(c) 2006-2007, Ext JS, LLC.
16679  *
16680  * Originally Released Under LGPL - original licence link has changed is not relivant.
16681  *
16682  * Fork - LGPL
16683  * <script type="text/javascript">
16684  */
16685
16686 /**
16687  * @class Roo.data.ArrayReader
16688  * @extends Roo.data.DataReader
16689  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16690  * Each element of that Array represents a row of data fields. The
16691  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16692  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16693  * <p>
16694  * Example code:.
16695  * <pre><code>
16696 var RecordDef = Roo.data.Record.create([
16697     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16698     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16699 ]);
16700 var myReader = new Roo.data.ArrayReader({
16701     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16702 }, RecordDef);
16703 </code></pre>
16704  * <p>
16705  * This would consume an Array like this:
16706  * <pre><code>
16707 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16708   </code></pre>
16709  
16710  * @constructor
16711  * Create a new JsonReader
16712  * @param {Object} meta Metadata configuration options.
16713  * @param {Object|Array} recordType Either an Array of field definition objects
16714  * 
16715  * @cfg {Array} fields Array of field definition objects
16716  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16717  * as specified to {@link Roo.data.Record#create},
16718  * or an {@link Roo.data.Record} object
16719  *
16720  * 
16721  * created using {@link Roo.data.Record#create}.
16722  */
16723 Roo.data.ArrayReader = function(meta, recordType)
16724 {    
16725     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16726 };
16727
16728 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16729     
16730       /**
16731      * Create a data block containing Roo.data.Records from an XML document.
16732      * @param {Object} o An Array of row objects which represents the dataset.
16733      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16734      * a cache of Roo.data.Records.
16735      */
16736     readRecords : function(o)
16737     {
16738         var sid = this.meta ? this.meta.id : null;
16739         var recordType = this.recordType, fields = recordType.prototype.fields;
16740         var records = [];
16741         var root = o;
16742         for(var i = 0; i < root.length; i++){
16743             var n = root[i];
16744             var values = {};
16745             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16746             for(var j = 0, jlen = fields.length; j < jlen; j++){
16747                 var f = fields.items[j];
16748                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16749                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16750                 v = f.convert(v);
16751                 values[f.name] = v;
16752             }
16753             var record = new recordType(values, id);
16754             record.json = n;
16755             records[records.length] = record;
16756         }
16757         return {
16758             records : records,
16759             totalRecords : records.length
16760         };
16761     },
16762     // used when loading children.. @see loadDataFromChildren
16763     toLoadData: function(rec)
16764     {
16765         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16766         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16767         
16768     }
16769     
16770     
16771 });/*
16772  * - LGPL
16773  * * 
16774  */
16775
16776 /**
16777  * @class Roo.bootstrap.form.ComboBox
16778  * @extends Roo.bootstrap.form.TriggerField
16779  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16780  * @cfg {Boolean} append (true|false) default false
16781  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16782  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16783  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16784  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16785  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16786  * @cfg {Boolean} animate default true
16787  * @cfg {Boolean} emptyResultText only for touch device
16788  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16789  * @cfg {String} emptyTitle default ''
16790  * @cfg {Number} width fixed with? experimental
16791  * @constructor
16792  * Create a new ComboBox.
16793  * @param {Object} config Configuration options
16794  */
16795 Roo.bootstrap.form.ComboBox = function(config){
16796     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16797     this.addEvents({
16798         /**
16799          * @event expand
16800          * Fires when the dropdown list is expanded
16801         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16802         */
16803         'expand' : true,
16804         /**
16805          * @event collapse
16806          * Fires when the dropdown list is collapsed
16807         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16808         */
16809         'collapse' : true,
16810         /**
16811          * @event beforeselect
16812          * Fires before a list item is selected. Return false to cancel the selection.
16813         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16814         * @param {Roo.data.Record} record The data record returned from the underlying store
16815         * @param {Number} index The index of the selected item in the dropdown list
16816         */
16817         'beforeselect' : true,
16818         /**
16819          * @event select
16820          * Fires when a list item is selected
16821         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16822         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16823         * @param {Number} index The index of the selected item in the dropdown list
16824         */
16825         'select' : true,
16826         /**
16827          * @event beforequery
16828          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16829          * The event object passed has these properties:
16830         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16831         * @param {String} query The query
16832         * @param {Boolean} forceAll true to force "all" query
16833         * @param {Boolean} cancel true to cancel the query
16834         * @param {Object} e The query event object
16835         */
16836         'beforequery': true,
16837          /**
16838          * @event add
16839          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16840         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16841         */
16842         'add' : true,
16843         /**
16844          * @event edit
16845          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16846         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16847         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16848         */
16849         'edit' : true,
16850         /**
16851          * @event remove
16852          * Fires when the remove value from the combobox array
16853         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16854         */
16855         'remove' : true,
16856         /**
16857          * @event afterremove
16858          * Fires when the remove value from the combobox array
16859         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16860         */
16861         'afterremove' : true,
16862         /**
16863          * @event specialfilter
16864          * Fires when specialfilter
16865             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16866             */
16867         'specialfilter' : true,
16868         /**
16869          * @event tick
16870          * Fires when tick the element
16871             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16872             */
16873         'tick' : true,
16874         /**
16875          * @event touchviewdisplay
16876          * Fires when touch view require special display (default is using displayField)
16877             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16878             * @param {Object} cfg set html .
16879             */
16880         'touchviewdisplay' : true
16881         
16882     });
16883     
16884     this.item = [];
16885     this.tickItems = [];
16886     
16887     this.selectedIndex = -1;
16888     if(this.mode == 'local'){
16889         if(config.queryDelay === undefined){
16890             this.queryDelay = 10;
16891         }
16892         if(config.minChars === undefined){
16893             this.minChars = 0;
16894         }
16895     }
16896 };
16897
16898 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16899      
16900     /**
16901      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16902      * rendering into an Roo.Editor, defaults to false)
16903      */
16904     /**
16905      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16906      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16907      */
16908     /**
16909      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16910      */
16911     /**
16912      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16913      * the dropdown list (defaults to undefined, with no header element)
16914      */
16915
16916      /**
16917      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16918      */
16919      
16920      /**
16921      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16922      */
16923     listWidth: undefined,
16924     /**
16925      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16926      * mode = 'remote' or 'text' if mode = 'local')
16927      */
16928     displayField: undefined,
16929     
16930     /**
16931      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16932      * mode = 'remote' or 'value' if mode = 'local'). 
16933      * Note: use of a valueField requires the user make a selection
16934      * in order for a value to be mapped.
16935      */
16936     valueField: undefined,
16937     /**
16938      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16939      */
16940     modalTitle : '',
16941     
16942     /**
16943      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16944      * field's data value (defaults to the underlying DOM element's name)
16945      */
16946     hiddenName: undefined,
16947     /**
16948      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16949      */
16950     listClass: '',
16951     /**
16952      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16953      */
16954     selectedClass: 'active',
16955     
16956     /**
16957      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16958      */
16959     shadow:'sides',
16960     /**
16961      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16962      * anchor positions (defaults to 'tl-bl')
16963      */
16964     listAlign: 'tl-bl?',
16965     /**
16966      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16967      */
16968     maxHeight: 300,
16969     /**
16970      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16971      * query specified by the allQuery config option (defaults to 'query')
16972      */
16973     triggerAction: 'query',
16974     /**
16975      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16976      * (defaults to 4, does not apply if editable = false)
16977      */
16978     minChars : 4,
16979     /**
16980      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16981      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16982      */
16983     typeAhead: false,
16984     /**
16985      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16986      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16987      */
16988     queryDelay: 500,
16989     /**
16990      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16991      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16992      */
16993     pageSize: 0,
16994     /**
16995      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16996      * when editable = true (defaults to false)
16997      */
16998     selectOnFocus:false,
16999     /**
17000      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17001      */
17002     queryParam: 'query',
17003     /**
17004      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17005      * when mode = 'remote' (defaults to 'Loading...')
17006      */
17007     loadingText: 'Loading...',
17008     /**
17009      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17010      */
17011     resizable: false,
17012     /**
17013      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17014      */
17015     handleHeight : 8,
17016     /**
17017      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17018      * traditional select (defaults to true)
17019      */
17020     editable: true,
17021     /**
17022      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17023      */
17024     allQuery: '',
17025     /**
17026      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17027      */
17028     mode: 'remote',
17029     /**
17030      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17031      * listWidth has a higher value)
17032      */
17033     minListWidth : 70,
17034     /**
17035      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17036      * allow the user to set arbitrary text into the field (defaults to false)
17037      */
17038     forceSelection:false,
17039     /**
17040      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17041      * if typeAhead = true (defaults to 250)
17042      */
17043     typeAheadDelay : 250,
17044     /**
17045      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17046      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17047      */
17048     valueNotFoundText : undefined,
17049     /**
17050      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17051      */
17052     blockFocus : false,
17053     
17054     /**
17055      * @cfg {Boolean} disableClear Disable showing of clear button.
17056      */
17057     disableClear : false,
17058     /**
17059      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17060      */
17061     alwaysQuery : false,
17062     
17063     /**
17064      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17065      */
17066     multiple : false,
17067     
17068     /**
17069      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17070      */
17071     invalidClass : "has-warning",
17072     
17073     /**
17074      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17075      */
17076     validClass : "has-success",
17077     
17078     /**
17079      * @cfg {Boolean} specialFilter (true|false) special filter default false
17080      */
17081     specialFilter : false,
17082     
17083     /**
17084      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17085      */
17086     mobileTouchView : true,
17087     
17088     /**
17089      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17090      */
17091     useNativeIOS : false,
17092     
17093     /**
17094      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17095      */
17096     mobile_restrict_height : false,
17097     
17098     ios_options : false,
17099     
17100     //private
17101     addicon : false,
17102     editicon: false,
17103     
17104     page: 0,
17105     hasQuery: false,
17106     append: false,
17107     loadNext: false,
17108     autoFocus : true,
17109     tickable : false,
17110     btnPosition : 'right',
17111     triggerList : true,
17112     showToggleBtn : true,
17113     animate : true,
17114     emptyResultText: 'Empty',
17115     triggerText : 'Select',
17116     emptyTitle : '',
17117     width : false,
17118     
17119     // element that contains real text value.. (when hidden is used..)
17120     
17121     getAutoCreate : function()
17122     {   
17123         var cfg = false;
17124         //render
17125         /*
17126          * Render classic select for iso
17127          */
17128         
17129         if(Roo.isIOS && this.useNativeIOS){
17130             cfg = this.getAutoCreateNativeIOS();
17131             return cfg;
17132         }
17133         
17134         /*
17135          * Touch Devices
17136          */
17137         
17138         if(Roo.isTouch && this.mobileTouchView){
17139             cfg = this.getAutoCreateTouchView();
17140             return cfg;;
17141         }
17142         
17143         /*
17144          *  Normal ComboBox
17145          */
17146         if(!this.tickable){
17147             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17148             return cfg;
17149         }
17150         
17151         /*
17152          *  ComboBox with tickable selections
17153          */
17154              
17155         var align = this.labelAlign || this.parentLabelAlign();
17156         
17157         cfg = {
17158             cls : 'form-group roo-combobox-tickable' //input-group
17159         };
17160         
17161         var btn_text_select = '';
17162         var btn_text_done = '';
17163         var btn_text_cancel = '';
17164         
17165         if (this.btn_text_show) {
17166             btn_text_select = 'Select';
17167             btn_text_done = 'Done';
17168             btn_text_cancel = 'Cancel'; 
17169         }
17170         
17171         var buttons = {
17172             tag : 'div',
17173             cls : 'tickable-buttons',
17174             cn : [
17175                 {
17176                     tag : 'button',
17177                     type : 'button',
17178                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17179                     //html : this.triggerText
17180                     html: btn_text_select
17181                 },
17182                 {
17183                     tag : 'button',
17184                     type : 'button',
17185                     name : 'ok',
17186                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17187                     //html : 'Done'
17188                     html: btn_text_done
17189                 },
17190                 {
17191                     tag : 'button',
17192                     type : 'button',
17193                     name : 'cancel',
17194                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17195                     //html : 'Cancel'
17196                     html: btn_text_cancel
17197                 }
17198             ]
17199         };
17200         
17201         if(this.editable){
17202             buttons.cn.unshift({
17203                 tag: 'input',
17204                 cls: 'roo-select2-search-field-input'
17205             });
17206         }
17207         
17208         var _this = this;
17209         
17210         Roo.each(buttons.cn, function(c){
17211             if (_this.size) {
17212                 c.cls += ' btn-' + _this.size;
17213             }
17214
17215             if (_this.disabled) {
17216                 c.disabled = true;
17217             }
17218         });
17219         
17220         var box = {
17221             tag: 'div',
17222             style : 'display: contents',
17223             cn: [
17224                 {
17225                     tag: 'input',
17226                     type : 'hidden',
17227                     cls: 'form-hidden-field'
17228                 },
17229                 {
17230                     tag: 'ul',
17231                     cls: 'roo-select2-choices',
17232                     cn:[
17233                         {
17234                             tag: 'li',
17235                             cls: 'roo-select2-search-field',
17236                             cn: [
17237                                 buttons
17238                             ]
17239                         }
17240                     ]
17241                 }
17242             ]
17243         };
17244         
17245         var combobox = {
17246             cls: 'roo-select2-container input-group roo-select2-container-multi',
17247             cn: [
17248                 
17249                 box
17250 //                {
17251 //                    tag: 'ul',
17252 //                    cls: 'typeahead typeahead-long dropdown-menu',
17253 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17254 //                }
17255             ]
17256         };
17257         
17258         if(this.hasFeedback && !this.allowBlank){
17259             
17260             var feedback = {
17261                 tag: 'span',
17262                 cls: 'glyphicon form-control-feedback'
17263             };
17264
17265             combobox.cn.push(feedback);
17266         }
17267         
17268         
17269         
17270         var indicator = {
17271             tag : 'i',
17272             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17273             tooltip : 'This field is required'
17274         };
17275         if (Roo.bootstrap.version == 4) {
17276             indicator = {
17277                 tag : 'i',
17278                 style : 'display:none'
17279             };
17280         }
17281         if (align ==='left' && this.fieldLabel.length) {
17282             
17283             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17284             
17285             cfg.cn = [
17286                 indicator,
17287                 {
17288                     tag: 'label',
17289                     'for' :  id,
17290                     cls : 'control-label col-form-label',
17291                     html : this.fieldLabel
17292
17293                 },
17294                 {
17295                     cls : "", 
17296                     cn: [
17297                         combobox
17298                     ]
17299                 }
17300
17301             ];
17302             
17303             var labelCfg = cfg.cn[1];
17304             var contentCfg = cfg.cn[2];
17305             
17306
17307             if(this.indicatorpos == 'right'){
17308                 
17309                 cfg.cn = [
17310                     {
17311                         tag: 'label',
17312                         'for' :  id,
17313                         cls : 'control-label col-form-label',
17314                         cn : [
17315                             {
17316                                 tag : 'span',
17317                                 html : this.fieldLabel
17318                             },
17319                             indicator
17320                         ]
17321                     },
17322                     {
17323                         cls : "",
17324                         cn: [
17325                             combobox
17326                         ]
17327                     }
17328
17329                 ];
17330                 
17331                 
17332                 
17333                 labelCfg = cfg.cn[0];
17334                 contentCfg = cfg.cn[1];
17335             
17336             }
17337             
17338             if(this.labelWidth > 12){
17339                 labelCfg.style = "width: " + this.labelWidth + 'px';
17340             }
17341             if(this.width * 1 > 0){
17342                 contentCfg.style = "width: " + this.width + 'px';
17343             }
17344             if(this.labelWidth < 13 && this.labelmd == 0){
17345                 this.labelmd = this.labelWidth;
17346             }
17347             
17348             if(this.labellg > 0){
17349                 labelCfg.cls += ' col-lg-' + this.labellg;
17350                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17351             }
17352             
17353             if(this.labelmd > 0){
17354                 labelCfg.cls += ' col-md-' + this.labelmd;
17355                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17356             }
17357             
17358             if(this.labelsm > 0){
17359                 labelCfg.cls += ' col-sm-' + this.labelsm;
17360                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17361             }
17362             
17363             if(this.labelxs > 0){
17364                 labelCfg.cls += ' col-xs-' + this.labelxs;
17365                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17366             }
17367                 
17368                 
17369         } else if ( this.fieldLabel.length) {
17370 //                Roo.log(" label");
17371                  cfg.cn = [
17372                    indicator,
17373                     {
17374                         tag: 'label',
17375                         //cls : 'input-group-addon',
17376                         html : this.fieldLabel
17377                     },
17378                     combobox
17379                 ];
17380                 
17381                 if(this.indicatorpos == 'right'){
17382                     cfg.cn = [
17383                         {
17384                             tag: 'label',
17385                             //cls : 'input-group-addon',
17386                             html : this.fieldLabel
17387                         },
17388                         indicator,
17389                         combobox
17390                     ];
17391                     
17392                 }
17393
17394         } else {
17395             
17396 //                Roo.log(" no label && no align");
17397                 cfg = combobox
17398                      
17399                 
17400         }
17401          
17402         var settings=this;
17403         ['xs','sm','md','lg'].map(function(size){
17404             if (settings[size]) {
17405                 cfg.cls += ' col-' + size + '-' + settings[size];
17406             }
17407         });
17408         
17409         return cfg;
17410         
17411     },
17412     
17413     _initEventsCalled : false,
17414     
17415     // private
17416     initEvents: function()
17417     {   
17418         if (this._initEventsCalled) { // as we call render... prevent looping...
17419             return;
17420         }
17421         this._initEventsCalled = true;
17422         
17423         if (!this.store) {
17424             throw "can not find store for combo";
17425         }
17426         
17427         this.indicator = this.indicatorEl();
17428         
17429         this.store = Roo.factory(this.store, Roo.data);
17430         this.store.parent = this;
17431         
17432         // if we are building from html. then this element is so complex, that we can not really
17433         // use the rendered HTML.
17434         // so we have to trash and replace the previous code.
17435         if (Roo.XComponent.build_from_html) {
17436             // remove this element....
17437             var e = this.el.dom, k=0;
17438             while (e ) { e = e.previousSibling;  ++k;}
17439
17440             this.el.remove();
17441             
17442             this.el=false;
17443             this.rendered = false;
17444             
17445             this.render(this.parent().getChildContainer(true), k);
17446         }
17447         
17448         if(Roo.isIOS && this.useNativeIOS){
17449             this.initIOSView();
17450             return;
17451         }
17452         
17453         /*
17454          * Touch Devices
17455          */
17456         
17457         if(Roo.isTouch && this.mobileTouchView){
17458             this.initTouchView();
17459             return;
17460         }
17461         
17462         if(this.tickable){
17463             this.initTickableEvents();
17464             return;
17465         }
17466         
17467         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17468         
17469         if(this.hiddenName){
17470             
17471             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17472             
17473             this.hiddenField.dom.value =
17474                 this.hiddenValue !== undefined ? this.hiddenValue :
17475                 this.value !== undefined ? this.value : '';
17476
17477             // prevent input submission
17478             this.el.dom.removeAttribute('name');
17479             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17480              
17481              
17482         }
17483         //if(Roo.isGecko){
17484         //    this.el.dom.setAttribute('autocomplete', 'off');
17485         //}
17486         
17487         var cls = 'x-combo-list';
17488         
17489         //this.list = new Roo.Layer({
17490         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17491         //});
17492         
17493         var _this = this;
17494         
17495         (function(){
17496             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17497             _this.list.setWidth(lw);
17498         }).defer(100);
17499         
17500         this.list.on('mouseover', this.onViewOver, this);
17501         this.list.on('mousemove', this.onViewMove, this);
17502         this.list.on('scroll', this.onViewScroll, this);
17503         
17504         /*
17505         this.list.swallowEvent('mousewheel');
17506         this.assetHeight = 0;
17507
17508         if(this.title){
17509             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17510             this.assetHeight += this.header.getHeight();
17511         }
17512
17513         this.innerList = this.list.createChild({cls:cls+'-inner'});
17514         this.innerList.on('mouseover', this.onViewOver, this);
17515         this.innerList.on('mousemove', this.onViewMove, this);
17516         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17517         
17518         if(this.allowBlank && !this.pageSize && !this.disableClear){
17519             this.footer = this.list.createChild({cls:cls+'-ft'});
17520             this.pageTb = new Roo.Toolbar(this.footer);
17521            
17522         }
17523         if(this.pageSize){
17524             this.footer = this.list.createChild({cls:cls+'-ft'});
17525             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17526                     {pageSize: this.pageSize});
17527             
17528         }
17529         
17530         if (this.pageTb && this.allowBlank && !this.disableClear) {
17531             var _this = this;
17532             this.pageTb.add(new Roo.Toolbar.Fill(), {
17533                 cls: 'x-btn-icon x-btn-clear',
17534                 text: '&#160;',
17535                 handler: function()
17536                 {
17537                     _this.collapse();
17538                     _this.clearValue();
17539                     _this.onSelect(false, -1);
17540                 }
17541             });
17542         }
17543         if (this.footer) {
17544             this.assetHeight += this.footer.getHeight();
17545         }
17546         */
17547             
17548         if(!this.tpl){
17549             this.tpl = Roo.bootstrap.version == 4 ?
17550                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17551                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17552         }
17553
17554         this.view = new Roo.View(this.list, this.tpl, {
17555             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17556         });
17557         //this.view.wrapEl.setDisplayed(false);
17558         this.view.on('click', this.onViewClick, this);
17559         
17560         
17561         this.store.on('beforeload', this.onBeforeLoad, this);
17562         this.store.on('load', this.onLoad, this);
17563         this.store.on('loadexception', this.onLoadException, this);
17564         /*
17565         if(this.resizable){
17566             this.resizer = new Roo.Resizable(this.list,  {
17567                pinned:true, handles:'se'
17568             });
17569             this.resizer.on('resize', function(r, w, h){
17570                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17571                 this.listWidth = w;
17572                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17573                 this.restrictHeight();
17574             }, this);
17575             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17576         }
17577         */
17578         if(!this.editable){
17579             this.editable = true;
17580             this.setEditable(false);
17581         }
17582         
17583         /*
17584         
17585         if (typeof(this.events.add.listeners) != 'undefined') {
17586             
17587             this.addicon = this.wrap.createChild(
17588                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17589        
17590             this.addicon.on('click', function(e) {
17591                 this.fireEvent('add', this);
17592             }, this);
17593         }
17594         if (typeof(this.events.edit.listeners) != 'undefined') {
17595             
17596             this.editicon = this.wrap.createChild(
17597                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17598             if (this.addicon) {
17599                 this.editicon.setStyle('margin-left', '40px');
17600             }
17601             this.editicon.on('click', function(e) {
17602                 
17603                 // we fire even  if inothing is selected..
17604                 this.fireEvent('edit', this, this.lastData );
17605                 
17606             }, this);
17607         }
17608         */
17609         
17610         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17611             "up" : function(e){
17612                 this.inKeyMode = true;
17613                 this.selectPrev();
17614             },
17615
17616             "down" : function(e){
17617                 if(!this.isExpanded()){
17618                     this.onTriggerClick();
17619                 }else{
17620                     this.inKeyMode = true;
17621                     this.selectNext();
17622                 }
17623             },
17624
17625             "enter" : function(e){
17626 //                this.onViewClick();
17627                 //return true;
17628                 this.collapse();
17629                 
17630                 if(this.fireEvent("specialkey", this, e)){
17631                     this.onViewClick(false);
17632                 }
17633                 
17634                 return true;
17635             },
17636
17637             "esc" : function(e){
17638                 this.collapse();
17639             },
17640
17641             "tab" : function(e){
17642                 this.collapse();
17643                 
17644                 if(this.fireEvent("specialkey", this, e)){
17645                     this.onViewClick(false);
17646                 }
17647                 
17648                 return true;
17649             },
17650
17651             scope : this,
17652
17653             doRelay : function(foo, bar, hname){
17654                 if(hname == 'down' || this.scope.isExpanded()){
17655                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17656                 }
17657                 return true;
17658             },
17659
17660             forceKeyDown: true
17661         });
17662         
17663         
17664         this.queryDelay = Math.max(this.queryDelay || 10,
17665                 this.mode == 'local' ? 10 : 250);
17666         
17667         
17668         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17669         
17670         if(this.typeAhead){
17671             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17672         }
17673         if(this.editable !== false){
17674             this.inputEl().on("keyup", this.onKeyUp, this);
17675         }
17676         if(this.forceSelection){
17677             this.inputEl().on('blur', this.doForce, this);
17678         }
17679         
17680         if(this.multiple){
17681             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17682             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17683         }
17684     },
17685     
17686     initTickableEvents: function()
17687     {   
17688         this.createList();
17689         
17690         if(this.hiddenName){
17691             
17692             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17693             
17694             this.hiddenField.dom.value =
17695                 this.hiddenValue !== undefined ? this.hiddenValue :
17696                 this.value !== undefined ? this.value : '';
17697
17698             // prevent input submission
17699             this.el.dom.removeAttribute('name');
17700             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17701              
17702              
17703         }
17704         
17705 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17706         
17707         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17708         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17709         if(this.triggerList){
17710             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17711         }
17712          
17713         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17714         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17715         
17716         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17717         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17718         
17719         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17720         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17721         
17722         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17723         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17724         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17725         
17726         this.okBtn.hide();
17727         this.cancelBtn.hide();
17728         
17729         var _this = this;
17730         
17731         (function(){
17732             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17733             _this.list.setWidth(lw);
17734         }).defer(100);
17735         
17736         this.list.on('mouseover', this.onViewOver, this);
17737         this.list.on('mousemove', this.onViewMove, this);
17738         
17739         this.list.on('scroll', this.onViewScroll, this);
17740         
17741         if(!this.tpl){
17742             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17743                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17744         }
17745
17746         this.view = new Roo.View(this.list, this.tpl, {
17747             singleSelect:true,
17748             tickable:true,
17749             parent:this,
17750             store: this.store,
17751             selectedClass: this.selectedClass
17752         });
17753         
17754         //this.view.wrapEl.setDisplayed(false);
17755         this.view.on('click', this.onViewClick, this);
17756         
17757         
17758         
17759         this.store.on('beforeload', this.onBeforeLoad, this);
17760         this.store.on('load', this.onLoad, this);
17761         this.store.on('loadexception', this.onLoadException, this);
17762         
17763         if(this.editable){
17764             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17765                 "up" : function(e){
17766                     this.inKeyMode = true;
17767                     this.selectPrev();
17768                 },
17769
17770                 "down" : function(e){
17771                     this.inKeyMode = true;
17772                     this.selectNext();
17773                 },
17774
17775                 "enter" : function(e){
17776                     if(this.fireEvent("specialkey", this, e)){
17777                         this.onViewClick(false);
17778                     }
17779                     
17780                     return true;
17781                 },
17782
17783                 "esc" : function(e){
17784                     this.onTickableFooterButtonClick(e, false, false);
17785                 },
17786
17787                 "tab" : function(e){
17788                     this.fireEvent("specialkey", this, e);
17789                     
17790                     this.onTickableFooterButtonClick(e, false, false);
17791                     
17792                     return true;
17793                 },
17794
17795                 scope : this,
17796
17797                 doRelay : function(e, fn, key){
17798                     if(this.scope.isExpanded()){
17799                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17800                     }
17801                     return true;
17802                 },
17803
17804                 forceKeyDown: true
17805             });
17806         }
17807         
17808         this.queryDelay = Math.max(this.queryDelay || 10,
17809                 this.mode == 'local' ? 10 : 250);
17810         
17811         
17812         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17813         
17814         if(this.typeAhead){
17815             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17816         }
17817         
17818         if(this.editable !== false){
17819             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17820         }
17821         
17822         this.indicator = this.indicatorEl();
17823         
17824         if(this.indicator){
17825             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17826             this.indicator.hide();
17827         }
17828         
17829     },
17830
17831     onDestroy : function(){
17832         if(this.view){
17833             this.view.setStore(null);
17834             this.view.el.removeAllListeners();
17835             this.view.el.remove();
17836             this.view.purgeListeners();
17837         }
17838         if(this.list){
17839             this.list.dom.innerHTML  = '';
17840         }
17841         
17842         if(this.store){
17843             this.store.un('beforeload', this.onBeforeLoad, this);
17844             this.store.un('load', this.onLoad, this);
17845             this.store.un('loadexception', this.onLoadException, this);
17846         }
17847         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17848     },
17849
17850     // private
17851     fireKey : function(e){
17852         if(e.isNavKeyPress() && !this.list.isVisible()){
17853             this.fireEvent("specialkey", this, e);
17854         }
17855     },
17856
17857     // private
17858     onResize: function(w, h)
17859     {
17860         
17861         
17862 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17863 //        
17864 //        if(typeof w != 'number'){
17865 //            // we do not handle it!?!?
17866 //            return;
17867 //        }
17868 //        var tw = this.trigger.getWidth();
17869 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17870 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17871 //        var x = w - tw;
17872 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17873 //            
17874 //        //this.trigger.setStyle('left', x+'px');
17875 //        
17876 //        if(this.list && this.listWidth === undefined){
17877 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17878 //            this.list.setWidth(lw);
17879 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17880 //        }
17881         
17882     
17883         
17884     },
17885
17886     /**
17887      * Allow or prevent the user from directly editing the field text.  If false is passed,
17888      * the user will only be able to select from the items defined in the dropdown list.  This method
17889      * is the runtime equivalent of setting the 'editable' config option at config time.
17890      * @param {Boolean} value True to allow the user to directly edit the field text
17891      */
17892     setEditable : function(value){
17893         if(value == this.editable){
17894             return;
17895         }
17896         this.editable = value;
17897         if(!value){
17898             this.inputEl().dom.setAttribute('readOnly', true);
17899             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17900             this.inputEl().addClass('x-combo-noedit');
17901         }else{
17902             this.inputEl().dom.removeAttribute('readOnly');
17903             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17904             this.inputEl().removeClass('x-combo-noedit');
17905         }
17906     },
17907
17908     // private
17909     
17910     onBeforeLoad : function(combo,opts){
17911         if(!this.hasFocus){
17912             return;
17913         }
17914          if (!opts.add) {
17915             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17916          }
17917         this.restrictHeight();
17918         this.selectedIndex = -1;
17919     },
17920
17921     // private
17922     onLoad : function(){
17923         
17924         this.hasQuery = false;
17925         
17926         if(!this.hasFocus){
17927             return;
17928         }
17929         
17930         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17931             this.loading.hide();
17932         }
17933         
17934         if(this.store.getCount() > 0){
17935             
17936             this.expand();
17937             this.restrictHeight();
17938             if(this.lastQuery == this.allQuery){
17939                 if(this.editable && !this.tickable){
17940                     this.inputEl().dom.select();
17941                 }
17942                 
17943                 if(
17944                     !this.selectByValue(this.value, true) &&
17945                     this.autoFocus && 
17946                     (
17947                         !this.store.lastOptions ||
17948                         typeof(this.store.lastOptions.add) == 'undefined' || 
17949                         this.store.lastOptions.add != true
17950                     )
17951                 ){
17952                     this.select(0, true);
17953                 }
17954             }else{
17955                 if(this.autoFocus){
17956                     this.selectNext();
17957                 }
17958                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17959                     this.taTask.delay(this.typeAheadDelay);
17960                 }
17961             }
17962         }else{
17963             this.onEmptyResults();
17964         }
17965         
17966         //this.el.focus();
17967     },
17968     // private
17969     onLoadException : function()
17970     {
17971         this.hasQuery = false;
17972         
17973         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17974             this.loading.hide();
17975         }
17976         
17977         if(this.tickable && this.editable){
17978             return;
17979         }
17980         
17981         this.collapse();
17982         // only causes errors at present
17983         //Roo.log(this.store.reader.jsonData);
17984         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17985             // fixme
17986             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17987         //}
17988         
17989         
17990     },
17991     // private
17992     onTypeAhead : function(){
17993         if(this.store.getCount() > 0){
17994             var r = this.store.getAt(0);
17995             var newValue = r.data[this.displayField];
17996             var len = newValue.length;
17997             var selStart = this.getRawValue().length;
17998             
17999             if(selStart != len){
18000                 this.setRawValue(newValue);
18001                 this.selectText(selStart, newValue.length);
18002             }
18003         }
18004     },
18005
18006     // private
18007     onSelect : function(record, index){
18008         
18009         if(this.fireEvent('beforeselect', this, record, index) !== false){
18010         
18011             this.setFromData(index > -1 ? record.data : false);
18012             
18013             this.collapse();
18014             this.fireEvent('select', this, record, index);
18015         }
18016     },
18017
18018     /**
18019      * Returns the currently selected field value or empty string if no value is set.
18020      * @return {String} value The selected value
18021      */
18022     getValue : function()
18023     {
18024         if(Roo.isIOS && this.useNativeIOS){
18025             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18026         }
18027         
18028         if(this.multiple){
18029             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18030         }
18031         
18032         if(this.valueField){
18033             return typeof this.value != 'undefined' ? this.value : '';
18034         }else{
18035             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18036         }
18037     },
18038     
18039     getRawValue : function()
18040     {
18041         if(Roo.isIOS && this.useNativeIOS){
18042             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18043         }
18044         
18045         var v = this.inputEl().getValue();
18046         
18047         return v;
18048     },
18049
18050     /**
18051      * Clears any text/value currently set in the field
18052      */
18053     clearValue : function(){
18054         
18055         if(this.hiddenField){
18056             this.hiddenField.dom.value = '';
18057         }
18058         this.value = '';
18059         this.setRawValue('');
18060         this.lastSelectionText = '';
18061         this.lastData = false;
18062         
18063         var close = this.closeTriggerEl();
18064         
18065         if(close){
18066             close.hide();
18067         }
18068         
18069         this.validate();
18070         
18071     },
18072
18073     /**
18074      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18075      * will be displayed in the field.  If the value does not match the data value of an existing item,
18076      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18077      * Otherwise the field will be blank (although the value will still be set).
18078      * @param {String} value The value to match
18079      */
18080     setValue : function(v)
18081     {
18082         if(Roo.isIOS && this.useNativeIOS){
18083             this.setIOSValue(v);
18084             return;
18085         }
18086         
18087         if(this.multiple){
18088             this.syncValue();
18089             return;
18090         }
18091         
18092         var text = v;
18093         if(this.valueField){
18094             var r = this.findRecord(this.valueField, v);
18095             if(r){
18096                 text = r.data[this.displayField];
18097             }else if(this.valueNotFoundText !== undefined){
18098                 text = this.valueNotFoundText;
18099             }
18100         }
18101         this.lastSelectionText = text;
18102         if(this.hiddenField){
18103             this.hiddenField.dom.value = v;
18104         }
18105         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18106         this.value = v;
18107         
18108         var close = this.closeTriggerEl();
18109         
18110         if(close){
18111             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18112         }
18113         
18114         this.validate();
18115     },
18116     /**
18117      * @property {Object} the last set data for the element
18118      */
18119     
18120     lastData : false,
18121     /**
18122      * Sets the value of the field based on a object which is related to the record format for the store.
18123      * @param {Object} value the value to set as. or false on reset?
18124      */
18125     setFromData : function(o){
18126         
18127         if(this.multiple){
18128             this.addItem(o);
18129             return;
18130         }
18131             
18132         var dv = ''; // display value
18133         var vv = ''; // value value..
18134         this.lastData = o;
18135         if (this.displayField) {
18136             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18137         } else {
18138             // this is an error condition!!!
18139             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18140         }
18141         
18142         if(this.valueField){
18143             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18144         }
18145         
18146         var close = this.closeTriggerEl();
18147         
18148         if(close){
18149             if(dv.length || vv * 1 > 0){
18150                 close.show() ;
18151                 this.blockFocus=true;
18152             } else {
18153                 close.hide();
18154             }             
18155         }
18156         
18157         if(this.hiddenField){
18158             this.hiddenField.dom.value = vv;
18159             
18160             this.lastSelectionText = dv;
18161             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18162             this.value = vv;
18163             return;
18164         }
18165         // no hidden field.. - we store the value in 'value', but still display
18166         // display field!!!!
18167         this.lastSelectionText = dv;
18168         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18169         this.value = vv;
18170         
18171         
18172         
18173     },
18174     // private
18175     reset : function(){
18176         // overridden so that last data is reset..
18177         
18178         if(this.multiple){
18179             this.clearItem();
18180             return;
18181         }
18182         
18183         this.setValue(this.originalValue);
18184         //this.clearInvalid();
18185         this.lastData = false;
18186         if (this.view) {
18187             this.view.clearSelections();
18188         }
18189         
18190         this.validate();
18191     },
18192     // private
18193     findRecord : function(prop, value){
18194         var record;
18195         if(this.store.getCount() > 0){
18196             this.store.each(function(r){
18197                 if(r.data[prop] == value){
18198                     record = r;
18199                     return false;
18200                 }
18201                 return true;
18202             });
18203         }
18204         return record;
18205     },
18206     
18207     getName: function()
18208     {
18209         // returns hidden if it's set..
18210         if (!this.rendered) {return ''};
18211         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18212         
18213     },
18214     // private
18215     onViewMove : function(e, t){
18216         this.inKeyMode = false;
18217     },
18218
18219     // private
18220     onViewOver : function(e, t){
18221         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18222             return;
18223         }
18224         var item = this.view.findItemFromChild(t);
18225         
18226         if(item){
18227             var index = this.view.indexOf(item);
18228             this.select(index, false);
18229         }
18230     },
18231
18232     // private
18233     onViewClick : function(view, doFocus, el, e)
18234     {
18235         var index = this.view.getSelectedIndexes()[0];
18236         
18237         var r = this.store.getAt(index);
18238         
18239         if(this.tickable){
18240             
18241             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18242                 return;
18243             }
18244             
18245             var rm = false;
18246             var _this = this;
18247             
18248             Roo.each(this.tickItems, function(v,k){
18249                 
18250                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18251                     Roo.log(v);
18252                     _this.tickItems.splice(k, 1);
18253                     
18254                     if(typeof(e) == 'undefined' && view == false){
18255                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18256                     }
18257                     
18258                     rm = true;
18259                     return;
18260                 }
18261             });
18262             
18263             if(rm){
18264                 return;
18265             }
18266             
18267             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18268                 this.tickItems.push(r.data);
18269             }
18270             
18271             if(typeof(e) == 'undefined' && view == false){
18272                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18273             }
18274                     
18275             return;
18276         }
18277         
18278         if(r){
18279             this.onSelect(r, index);
18280         }
18281         if(doFocus !== false && !this.blockFocus){
18282             this.inputEl().focus();
18283         }
18284     },
18285
18286     // private
18287     restrictHeight : function(){
18288         //this.innerList.dom.style.height = '';
18289         //var inner = this.innerList.dom;
18290         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18291         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18292         //this.list.beginUpdate();
18293         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18294         this.list.alignTo(this.inputEl(), this.listAlign);
18295         this.list.alignTo(this.inputEl(), this.listAlign);
18296         //this.list.endUpdate();
18297     },
18298
18299     // private
18300     onEmptyResults : function(){
18301         
18302         if(this.tickable && this.editable){
18303             this.hasFocus = false;
18304             this.restrictHeight();
18305             return;
18306         }
18307         
18308         this.collapse();
18309     },
18310
18311     /**
18312      * Returns true if the dropdown list is expanded, else false.
18313      */
18314     isExpanded : function(){
18315         return this.list.isVisible();
18316     },
18317
18318     /**
18319      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18320      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18321      * @param {String} value The data value of the item to select
18322      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18323      * selected item if it is not currently in view (defaults to true)
18324      * @return {Boolean} True if the value matched an item in the list, else false
18325      */
18326     selectByValue : function(v, scrollIntoView){
18327         if(v !== undefined && v !== null){
18328             var r = this.findRecord(this.valueField || this.displayField, v);
18329             if(r){
18330                 this.select(this.store.indexOf(r), scrollIntoView);
18331                 return true;
18332             }
18333         }
18334         return false;
18335     },
18336
18337     /**
18338      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18339      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18340      * @param {Number} index The zero-based index of the list item to select
18341      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18342      * selected item if it is not currently in view (defaults to true)
18343      */
18344     select : function(index, scrollIntoView){
18345         this.selectedIndex = index;
18346         this.view.select(index);
18347         if(scrollIntoView !== false){
18348             var el = this.view.getNode(index);
18349             /*
18350              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18351              */
18352             if(el){
18353                 this.list.scrollChildIntoView(el, false);
18354             }
18355         }
18356     },
18357
18358     // private
18359     selectNext : function(){
18360         var ct = this.store.getCount();
18361         if(ct > 0){
18362             if(this.selectedIndex == -1){
18363                 this.select(0);
18364             }else if(this.selectedIndex < ct-1){
18365                 this.select(this.selectedIndex+1);
18366             }
18367         }
18368     },
18369
18370     // private
18371     selectPrev : function(){
18372         var ct = this.store.getCount();
18373         if(ct > 0){
18374             if(this.selectedIndex == -1){
18375                 this.select(0);
18376             }else if(this.selectedIndex != 0){
18377                 this.select(this.selectedIndex-1);
18378             }
18379         }
18380     },
18381
18382     // private
18383     onKeyUp : function(e){
18384         if(this.editable !== false && !e.isSpecialKey()){
18385             this.lastKey = e.getKey();
18386             this.dqTask.delay(this.queryDelay);
18387         }
18388     },
18389
18390     // private
18391     validateBlur : function(){
18392         return !this.list || !this.list.isVisible();   
18393     },
18394
18395     // private
18396     initQuery : function(){
18397         
18398         var v = this.getRawValue();
18399         
18400         if(this.tickable && this.editable){
18401             v = this.tickableInputEl().getValue();
18402         }
18403         
18404         this.doQuery(v);
18405     },
18406
18407     // private
18408     doForce : function(){
18409         if(this.inputEl().dom.value.length > 0){
18410             this.inputEl().dom.value =
18411                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18412              
18413         }
18414     },
18415
18416     /**
18417      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18418      * query allowing the query action to be canceled if needed.
18419      * @param {String} query The SQL query to execute
18420      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18421      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18422      * saved in the current store (defaults to false)
18423      */
18424     doQuery : function(q, forceAll){
18425         
18426         if(q === undefined || q === null){
18427             q = '';
18428         }
18429         var qe = {
18430             query: q,
18431             forceAll: forceAll,
18432             combo: this,
18433             cancel:false
18434         };
18435         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18436             return false;
18437         }
18438         q = qe.query;
18439         
18440         forceAll = qe.forceAll;
18441         if(forceAll === true || (q.length >= this.minChars)){
18442             
18443             this.hasQuery = true;
18444             
18445             if(this.lastQuery != q || this.alwaysQuery){
18446                 this.lastQuery = q;
18447                 if(this.mode == 'local'){
18448                     this.selectedIndex = -1;
18449                     if(forceAll){
18450                         this.store.clearFilter();
18451                     }else{
18452                         
18453                         if(this.specialFilter){
18454                             this.fireEvent('specialfilter', this);
18455                             this.onLoad();
18456                             return;
18457                         }
18458                         
18459                         this.store.filter(this.displayField, q);
18460                     }
18461                     
18462                     this.store.fireEvent("datachanged", this.store);
18463                     
18464                     this.onLoad();
18465                     
18466                     
18467                 }else{
18468                     
18469                     this.store.baseParams[this.queryParam] = q;
18470                     
18471                     var options = {params : this.getParams(q)};
18472                     
18473                     if(this.loadNext){
18474                         options.add = true;
18475                         options.params.start = this.page * this.pageSize;
18476                     }
18477                     
18478                     this.store.load(options);
18479                     
18480                     /*
18481                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18482                      *  we should expand the list on onLoad
18483                      *  so command out it
18484                      */
18485 //                    this.expand();
18486                 }
18487             }else{
18488                 this.selectedIndex = -1;
18489                 this.onLoad();   
18490             }
18491         }
18492         
18493         this.loadNext = false;
18494     },
18495     
18496     // private
18497     getParams : function(q){
18498         var p = {};
18499         //p[this.queryParam] = q;
18500         
18501         if(this.pageSize){
18502             p.start = 0;
18503             p.limit = this.pageSize;
18504         }
18505         return p;
18506     },
18507
18508     /**
18509      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18510      */
18511     collapse : function(){
18512         if(!this.isExpanded()){
18513             return;
18514         }
18515         
18516         this.list.hide();
18517         
18518         this.hasFocus = false;
18519         
18520         if(this.tickable){
18521             this.okBtn.hide();
18522             this.cancelBtn.hide();
18523             this.trigger.show();
18524             
18525             if(this.editable){
18526                 this.tickableInputEl().dom.value = '';
18527                 this.tickableInputEl().blur();
18528             }
18529             
18530         }
18531         
18532         Roo.get(document).un('mousedown', this.collapseIf, this);
18533         Roo.get(document).un('mousewheel', this.collapseIf, this);
18534         if (!this.editable) {
18535             Roo.get(document).un('keydown', this.listKeyPress, this);
18536         }
18537         this.fireEvent('collapse', this);
18538         
18539         this.validate();
18540     },
18541
18542     // private
18543     collapseIf : function(e){
18544         var in_combo  = e.within(this.el);
18545         var in_list =  e.within(this.list);
18546         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18547         
18548         if (in_combo || in_list || is_list) {
18549             //e.stopPropagation();
18550             return;
18551         }
18552         
18553         if(this.tickable){
18554             this.onTickableFooterButtonClick(e, false, false);
18555         }
18556
18557         this.collapse();
18558         
18559     },
18560
18561     /**
18562      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18563      */
18564     expand : function(){
18565        
18566         if(this.isExpanded() || !this.hasFocus){
18567             return;
18568         }
18569         
18570         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18571         this.list.setWidth(lw);
18572         
18573         Roo.log('expand');
18574         
18575         this.list.show();
18576         
18577         this.restrictHeight();
18578         
18579         if(this.tickable){
18580             
18581             this.tickItems = Roo.apply([], this.item);
18582             
18583             this.okBtn.show();
18584             this.cancelBtn.show();
18585             this.trigger.hide();
18586             
18587             if(this.editable){
18588                 this.tickableInputEl().focus();
18589             }
18590             
18591         }
18592         
18593         Roo.get(document).on('mousedown', this.collapseIf, this);
18594         Roo.get(document).on('mousewheel', this.collapseIf, this);
18595         if (!this.editable) {
18596             Roo.get(document).on('keydown', this.listKeyPress, this);
18597         }
18598         
18599         this.fireEvent('expand', this);
18600     },
18601
18602     // private
18603     // Implements the default empty TriggerField.onTriggerClick function
18604     onTriggerClick : function(e)
18605     {
18606         Roo.log('trigger click');
18607         
18608         if(this.disabled || !this.triggerList){
18609             return;
18610         }
18611         
18612         this.page = 0;
18613         this.loadNext = false;
18614         
18615         if(this.isExpanded()){
18616             this.collapse();
18617             if (!this.blockFocus) {
18618                 this.inputEl().focus();
18619             }
18620             
18621         }else {
18622             this.hasFocus = true;
18623             if(this.triggerAction == 'all') {
18624                 this.doQuery(this.allQuery, true);
18625             } else {
18626                 this.doQuery(this.getRawValue());
18627             }
18628             if (!this.blockFocus) {
18629                 this.inputEl().focus();
18630             }
18631         }
18632     },
18633     
18634     onTickableTriggerClick : function(e)
18635     {
18636         if(this.disabled){
18637             return;
18638         }
18639         
18640         this.page = 0;
18641         this.loadNext = false;
18642         this.hasFocus = true;
18643         
18644         if(this.triggerAction == 'all') {
18645             this.doQuery(this.allQuery, true);
18646         } else {
18647             this.doQuery(this.getRawValue());
18648         }
18649     },
18650     
18651     onSearchFieldClick : function(e)
18652     {
18653         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18654             this.onTickableFooterButtonClick(e, false, false);
18655             return;
18656         }
18657         
18658         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18659             return;
18660         }
18661         
18662         this.page = 0;
18663         this.loadNext = false;
18664         this.hasFocus = true;
18665         
18666         if(this.triggerAction == 'all') {
18667             this.doQuery(this.allQuery, true);
18668         } else {
18669             this.doQuery(this.getRawValue());
18670         }
18671     },
18672     
18673     listKeyPress : function(e)
18674     {
18675         //Roo.log('listkeypress');
18676         // scroll to first matching element based on key pres..
18677         if (e.isSpecialKey()) {
18678             return false;
18679         }
18680         var k = String.fromCharCode(e.getKey()).toUpperCase();
18681         //Roo.log(k);
18682         var match  = false;
18683         var csel = this.view.getSelectedNodes();
18684         var cselitem = false;
18685         if (csel.length) {
18686             var ix = this.view.indexOf(csel[0]);
18687             cselitem  = this.store.getAt(ix);
18688             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18689                 cselitem = false;
18690             }
18691             
18692         }
18693         
18694         this.store.each(function(v) { 
18695             if (cselitem) {
18696                 // start at existing selection.
18697                 if (cselitem.id == v.id) {
18698                     cselitem = false;
18699                 }
18700                 return true;
18701             }
18702                 
18703             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18704                 match = this.store.indexOf(v);
18705                 return false;
18706             }
18707             return true;
18708         }, this);
18709         
18710         if (match === false) {
18711             return true; // no more action?
18712         }
18713         // scroll to?
18714         this.view.select(match);
18715         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18716         sn.scrollIntoView(sn.dom.parentNode, false);
18717     },
18718     
18719     onViewScroll : function(e, t){
18720         
18721         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){
18722             return;
18723         }
18724         
18725         this.hasQuery = true;
18726         
18727         this.loading = this.list.select('.loading', true).first();
18728         
18729         if(this.loading === null){
18730             this.list.createChild({
18731                 tag: 'div',
18732                 cls: 'loading roo-select2-more-results roo-select2-active',
18733                 html: 'Loading more results...'
18734             });
18735             
18736             this.loading = this.list.select('.loading', true).first();
18737             
18738             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18739             
18740             this.loading.hide();
18741         }
18742         
18743         this.loading.show();
18744         
18745         var _combo = this;
18746         
18747         this.page++;
18748         this.loadNext = true;
18749         
18750         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18751         
18752         return;
18753     },
18754     
18755     addItem : function(o)
18756     {   
18757         var dv = ''; // display value
18758         
18759         if (this.displayField) {
18760             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18761         } else {
18762             // this is an error condition!!!
18763             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18764         }
18765         
18766         if(!dv.length){
18767             return;
18768         }
18769         
18770         var choice = this.choices.createChild({
18771             tag: 'li',
18772             cls: 'roo-select2-search-choice',
18773             cn: [
18774                 {
18775                     tag: 'div',
18776                     html: dv
18777                 },
18778                 {
18779                     tag: 'a',
18780                     href: '#',
18781                     cls: 'roo-select2-search-choice-close fa fa-times',
18782                     tabindex: '-1'
18783                 }
18784             ]
18785             
18786         }, this.searchField);
18787         
18788         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18789         
18790         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18791         
18792         this.item.push(o);
18793         
18794         this.lastData = o;
18795         
18796         this.syncValue();
18797         
18798         this.inputEl().dom.value = '';
18799         
18800         this.validate();
18801     },
18802     
18803     onRemoveItem : function(e, _self, o)
18804     {
18805         e.preventDefault();
18806         
18807         this.lastItem = Roo.apply([], this.item);
18808         
18809         var index = this.item.indexOf(o.data) * 1;
18810         
18811         if( index < 0){
18812             Roo.log('not this item?!');
18813             return;
18814         }
18815         
18816         this.item.splice(index, 1);
18817         o.item.remove();
18818         
18819         this.syncValue();
18820         
18821         this.fireEvent('remove', this, e);
18822         
18823         this.validate();
18824         
18825     },
18826     
18827     syncValue : function()
18828     {
18829         if(!this.item.length){
18830             this.clearValue();
18831             return;
18832         }
18833             
18834         var value = [];
18835         var _this = this;
18836         Roo.each(this.item, function(i){
18837             if(_this.valueField){
18838                 value.push(i[_this.valueField]);
18839                 return;
18840             }
18841
18842             value.push(i);
18843         });
18844
18845         this.value = value.join(',');
18846
18847         if(this.hiddenField){
18848             this.hiddenField.dom.value = this.value;
18849         }
18850         
18851         this.store.fireEvent("datachanged", this.store);
18852         
18853         this.validate();
18854     },
18855     
18856     clearItem : function()
18857     {
18858         if(!this.multiple){
18859             return;
18860         }
18861         
18862         this.item = [];
18863         
18864         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18865            c.remove();
18866         });
18867         
18868         this.syncValue();
18869         
18870         this.validate();
18871         
18872         if(this.tickable && !Roo.isTouch){
18873             this.view.refresh();
18874         }
18875     },
18876     
18877     inputEl: function ()
18878     {
18879         if(Roo.isIOS && this.useNativeIOS){
18880             return this.el.select('select.roo-ios-select', true).first();
18881         }
18882         
18883         if(Roo.isTouch && this.mobileTouchView){
18884             return this.el.select('input.form-control',true).first();
18885         }
18886         
18887         if(this.tickable){
18888             return this.searchField;
18889         }
18890         
18891         return this.el.select('input.form-control',true).first();
18892     },
18893     
18894     onTickableFooterButtonClick : function(e, btn, el)
18895     {
18896         e.preventDefault();
18897         
18898         this.lastItem = Roo.apply([], this.item);
18899         
18900         if(btn && btn.name == 'cancel'){
18901             this.tickItems = Roo.apply([], this.item);
18902             this.collapse();
18903             return;
18904         }
18905         
18906         this.clearItem();
18907         
18908         var _this = this;
18909         
18910         Roo.each(this.tickItems, function(o){
18911             _this.addItem(o);
18912         });
18913         
18914         this.collapse();
18915         
18916     },
18917     
18918     validate : function()
18919     {
18920         if(this.getVisibilityEl().hasClass('hidden')){
18921             return true;
18922         }
18923         
18924         var v = this.getRawValue();
18925         
18926         if(this.multiple){
18927             v = this.getValue();
18928         }
18929         
18930         if(this.disabled || this.allowBlank || v.length){
18931             this.markValid();
18932             return true;
18933         }
18934         
18935         this.markInvalid();
18936         return false;
18937     },
18938     
18939     tickableInputEl : function()
18940     {
18941         if(!this.tickable || !this.editable){
18942             return this.inputEl();
18943         }
18944         
18945         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18946     },
18947     
18948     
18949     getAutoCreateTouchView : function()
18950     {
18951         var id = Roo.id();
18952         
18953         var cfg = {
18954             cls: 'form-group' //input-group
18955         };
18956         
18957         var input =  {
18958             tag: 'input',
18959             id : id,
18960             type : this.inputType,
18961             cls : 'form-control x-combo-noedit',
18962             autocomplete: 'new-password',
18963             placeholder : this.placeholder || '',
18964             readonly : true
18965         };
18966         
18967         if (this.name) {
18968             input.name = this.name;
18969         }
18970         
18971         if (this.size) {
18972             input.cls += ' input-' + this.size;
18973         }
18974         
18975         if (this.disabled) {
18976             input.disabled = true;
18977         }
18978         
18979         var inputblock = {
18980             cls : 'roo-combobox-wrap',
18981             cn : [
18982                 input
18983             ]
18984         };
18985         
18986         if(this.before){
18987             inputblock.cls += ' input-group';
18988             
18989             inputblock.cn.unshift({
18990                 tag :'span',
18991                 cls : 'input-group-addon input-group-prepend input-group-text',
18992                 html : this.before
18993             });
18994         }
18995         
18996         if(this.removable && !this.multiple){
18997             inputblock.cls += ' roo-removable';
18998             
18999             inputblock.cn.push({
19000                 tag: 'button',
19001                 html : 'x',
19002                 cls : 'roo-combo-removable-btn close'
19003             });
19004         }
19005
19006         if(this.hasFeedback && !this.allowBlank){
19007             
19008             inputblock.cls += ' has-feedback';
19009             
19010             inputblock.cn.push({
19011                 tag: 'span',
19012                 cls: 'glyphicon form-control-feedback'
19013             });
19014             
19015         }
19016         
19017         if (this.after) {
19018             
19019             inputblock.cls += (this.before) ? '' : ' input-group';
19020             
19021             inputblock.cn.push({
19022                 tag :'span',
19023                 cls : 'input-group-addon input-group-append input-group-text',
19024                 html : this.after
19025             });
19026         }
19027
19028         
19029         var ibwrap = inputblock;
19030         
19031         if(this.multiple){
19032             ibwrap = {
19033                 tag: 'ul',
19034                 cls: 'roo-select2-choices',
19035                 cn:[
19036                     {
19037                         tag: 'li',
19038                         cls: 'roo-select2-search-field',
19039                         cn: [
19040
19041                             inputblock
19042                         ]
19043                     }
19044                 ]
19045             };
19046         
19047             
19048         }
19049         
19050         var combobox = {
19051             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19052             cn: [
19053                 {
19054                     tag: 'input',
19055                     type : 'hidden',
19056                     cls: 'form-hidden-field'
19057                 },
19058                 ibwrap
19059             ]
19060         };
19061         
19062         if(!this.multiple && this.showToggleBtn){
19063             
19064             var caret = {
19065                 cls: 'caret'
19066             };
19067             
19068             if (this.caret != false) {
19069                 caret = {
19070                      tag: 'i',
19071                      cls: 'fa fa-' + this.caret
19072                 };
19073                 
19074             }
19075             
19076             combobox.cn.push({
19077                 tag :'span',
19078                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19079                 cn : [
19080                     Roo.bootstrap.version == 3 ? caret : '',
19081                     {
19082                         tag: 'span',
19083                         cls: 'combobox-clear',
19084                         cn  : [
19085                             {
19086                                 tag : 'i',
19087                                 cls: 'icon-remove'
19088                             }
19089                         ]
19090                     }
19091                 ]
19092
19093             })
19094         }
19095         
19096         if(this.multiple){
19097             combobox.cls += ' roo-select2-container-multi';
19098         }
19099         
19100         var required =  this.allowBlank ?  {
19101                     tag : 'i',
19102                     style: 'display: none'
19103                 } : {
19104                    tag : 'i',
19105                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19106                    tooltip : 'This field is required'
19107                 };
19108         
19109         var align = this.labelAlign || this.parentLabelAlign();
19110         
19111         if (align ==='left' && this.fieldLabel.length) {
19112
19113             cfg.cn = [
19114                 required,
19115                 {
19116                     tag: 'label',
19117                     cls : 'control-label col-form-label',
19118                     html : this.fieldLabel
19119
19120                 },
19121                 {
19122                     cls : 'roo-combobox-wrap ', 
19123                     cn: [
19124                         combobox
19125                     ]
19126                 }
19127             ];
19128             
19129             var labelCfg = cfg.cn[1];
19130             var contentCfg = cfg.cn[2];
19131             
19132
19133             if(this.indicatorpos == 'right'){
19134                 cfg.cn = [
19135                     {
19136                         tag: 'label',
19137                         'for' :  id,
19138                         cls : 'control-label col-form-label',
19139                         cn : [
19140                             {
19141                                 tag : 'span',
19142                                 html : this.fieldLabel
19143                             },
19144                             required
19145                         ]
19146                     },
19147                     {
19148                         cls : "roo-combobox-wrap ",
19149                         cn: [
19150                             combobox
19151                         ]
19152                     }
19153
19154                 ];
19155                 
19156                 labelCfg = cfg.cn[0];
19157                 contentCfg = cfg.cn[1];
19158             }
19159             
19160            
19161             
19162             if(this.labelWidth > 12){
19163                 labelCfg.style = "width: " + this.labelWidth + 'px';
19164             }
19165            
19166             if(this.labelWidth < 13 && this.labelmd == 0){
19167                 this.labelmd = this.labelWidth;
19168             }
19169             
19170             if(this.labellg > 0){
19171                 labelCfg.cls += ' col-lg-' + this.labellg;
19172                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19173             }
19174             
19175             if(this.labelmd > 0){
19176                 labelCfg.cls += ' col-md-' + this.labelmd;
19177                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19178             }
19179             
19180             if(this.labelsm > 0){
19181                 labelCfg.cls += ' col-sm-' + this.labelsm;
19182                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19183             }
19184             
19185             if(this.labelxs > 0){
19186                 labelCfg.cls += ' col-xs-' + this.labelxs;
19187                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19188             }
19189                 
19190                 
19191         } else if ( this.fieldLabel.length) {
19192             cfg.cn = [
19193                required,
19194                 {
19195                     tag: 'label',
19196                     cls : 'control-label',
19197                     html : this.fieldLabel
19198
19199                 },
19200                 {
19201                     cls : '', 
19202                     cn: [
19203                         combobox
19204                     ]
19205                 }
19206             ];
19207             
19208             if(this.indicatorpos == 'right'){
19209                 cfg.cn = [
19210                     {
19211                         tag: 'label',
19212                         cls : 'control-label',
19213                         html : this.fieldLabel,
19214                         cn : [
19215                             required
19216                         ]
19217                     },
19218                     {
19219                         cls : '', 
19220                         cn: [
19221                             combobox
19222                         ]
19223                     }
19224                 ];
19225             }
19226         } else {
19227             cfg.cn = combobox;    
19228         }
19229         
19230         
19231         var settings = this;
19232         
19233         ['xs','sm','md','lg'].map(function(size){
19234             if (settings[size]) {
19235                 cfg.cls += ' col-' + size + '-' + settings[size];
19236             }
19237         });
19238         
19239         return cfg;
19240     },
19241     
19242     initTouchView : function()
19243     {
19244         this.renderTouchView();
19245         
19246         this.touchViewEl.on('scroll', function(){
19247             this.el.dom.scrollTop = 0;
19248         }, this);
19249         
19250         this.originalValue = this.getValue();
19251         
19252         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19253         
19254         this.inputEl().on("click", this.showTouchView, this);
19255         if (this.triggerEl) {
19256             this.triggerEl.on("click", this.showTouchView, this);
19257         }
19258         
19259         
19260         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19261         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19262         
19263         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19264         
19265         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19266         this.store.on('load', this.onTouchViewLoad, this);
19267         this.store.on('loadexception', this.onTouchViewLoadException, this);
19268         
19269         if(this.hiddenName){
19270             
19271             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19272             
19273             this.hiddenField.dom.value =
19274                 this.hiddenValue !== undefined ? this.hiddenValue :
19275                 this.value !== undefined ? this.value : '';
19276         
19277             this.el.dom.removeAttribute('name');
19278             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19279         }
19280         
19281         if(this.multiple){
19282             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19283             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19284         }
19285         
19286         if(this.removable && !this.multiple){
19287             var close = this.closeTriggerEl();
19288             if(close){
19289                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19290                 close.on('click', this.removeBtnClick, this, close);
19291             }
19292         }
19293         /*
19294          * fix the bug in Safari iOS8
19295          */
19296         this.inputEl().on("focus", function(e){
19297             document.activeElement.blur();
19298         }, this);
19299         
19300         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19301         
19302         return;
19303         
19304         
19305     },
19306     
19307     renderTouchView : function()
19308     {
19309         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19310         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19311         
19312         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19313         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19314         
19315         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19316         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19317         this.touchViewBodyEl.setStyle('overflow', 'auto');
19318         
19319         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19320         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19321         
19322         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19323         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19324         
19325     },
19326     
19327     showTouchView : function()
19328     {
19329         if(this.disabled){
19330             return;
19331         }
19332         
19333         this.touchViewHeaderEl.hide();
19334
19335         if(this.modalTitle.length){
19336             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19337             this.touchViewHeaderEl.show();
19338         }
19339
19340         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19341         this.touchViewEl.show();
19342
19343         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19344         
19345         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19346         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19347
19348         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19349
19350         if(this.modalTitle.length){
19351             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19352         }
19353         
19354         this.touchViewBodyEl.setHeight(bodyHeight);
19355
19356         if(this.animate){
19357             var _this = this;
19358             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19359         }else{
19360             this.touchViewEl.addClass(['in','show']);
19361         }
19362         
19363         if(this._touchViewMask){
19364             Roo.get(document.body).addClass("x-body-masked");
19365             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19366             this._touchViewMask.setStyle('z-index', 10000);
19367             this._touchViewMask.addClass('show');
19368         }
19369         
19370         this.doTouchViewQuery();
19371         
19372     },
19373     
19374     hideTouchView : function()
19375     {
19376         this.touchViewEl.removeClass(['in','show']);
19377
19378         if(this.animate){
19379             var _this = this;
19380             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19381         }else{
19382             this.touchViewEl.setStyle('display', 'none');
19383         }
19384         
19385         if(this._touchViewMask){
19386             this._touchViewMask.removeClass('show');
19387             Roo.get(document.body).removeClass("x-body-masked");
19388         }
19389     },
19390     
19391     setTouchViewValue : function()
19392     {
19393         if(this.multiple){
19394             this.clearItem();
19395         
19396             var _this = this;
19397
19398             Roo.each(this.tickItems, function(o){
19399                 this.addItem(o);
19400             }, this);
19401         }
19402         
19403         this.hideTouchView();
19404     },
19405     
19406     doTouchViewQuery : function()
19407     {
19408         var qe = {
19409             query: '',
19410             forceAll: true,
19411             combo: this,
19412             cancel:false
19413         };
19414         
19415         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19416             return false;
19417         }
19418         
19419         if(!this.alwaysQuery || this.mode == 'local'){
19420             this.onTouchViewLoad();
19421             return;
19422         }
19423         
19424         this.store.load();
19425     },
19426     
19427     onTouchViewBeforeLoad : function(combo,opts)
19428     {
19429         return;
19430     },
19431
19432     // private
19433     onTouchViewLoad : function()
19434     {
19435         if(this.store.getCount() < 1){
19436             this.onTouchViewEmptyResults();
19437             return;
19438         }
19439         
19440         this.clearTouchView();
19441         
19442         var rawValue = this.getRawValue();
19443         
19444         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19445         
19446         this.tickItems = [];
19447         
19448         this.store.data.each(function(d, rowIndex){
19449             var row = this.touchViewListGroup.createChild(template);
19450             
19451             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19452                 row.addClass(d.data.cls);
19453             }
19454             
19455             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19456                 var cfg = {
19457                     data : d.data,
19458                     html : d.data[this.displayField]
19459                 };
19460                 
19461                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19462                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19463                 }
19464             }
19465             row.removeClass('selected');
19466             if(!this.multiple && this.valueField &&
19467                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19468             {
19469                 // radio buttons..
19470                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19471                 row.addClass('selected');
19472             }
19473             
19474             if(this.multiple && this.valueField &&
19475                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19476             {
19477                 
19478                 // checkboxes...
19479                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19480                 this.tickItems.push(d.data);
19481             }
19482             
19483             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19484             
19485         }, this);
19486         
19487         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19488         
19489         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19490
19491         if(this.modalTitle.length){
19492             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19493         }
19494
19495         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19496         
19497         if(this.mobile_restrict_height && listHeight < bodyHeight){
19498             this.touchViewBodyEl.setHeight(listHeight);
19499         }
19500         
19501         var _this = this;
19502         
19503         if(firstChecked && listHeight > bodyHeight){
19504             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19505         }
19506         
19507     },
19508     
19509     onTouchViewLoadException : function()
19510     {
19511         this.hideTouchView();
19512     },
19513     
19514     onTouchViewEmptyResults : function()
19515     {
19516         this.clearTouchView();
19517         
19518         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19519         
19520         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19521         
19522     },
19523     
19524     clearTouchView : function()
19525     {
19526         this.touchViewListGroup.dom.innerHTML = '';
19527     },
19528     
19529     onTouchViewClick : function(e, el, o)
19530     {
19531         e.preventDefault();
19532         
19533         var row = o.row;
19534         var rowIndex = o.rowIndex;
19535         
19536         var r = this.store.getAt(rowIndex);
19537         
19538         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19539             
19540             if(!this.multiple){
19541                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19542                     c.dom.removeAttribute('checked');
19543                 }, this);
19544
19545                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19546
19547                 this.setFromData(r.data);
19548
19549                 var close = this.closeTriggerEl();
19550
19551                 if(close){
19552                     close.show();
19553                 }
19554
19555                 this.hideTouchView();
19556
19557                 this.fireEvent('select', this, r, rowIndex);
19558
19559                 return;
19560             }
19561
19562             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19563                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19564                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19565                 return;
19566             }
19567
19568             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19569             this.addItem(r.data);
19570             this.tickItems.push(r.data);
19571         }
19572     },
19573     
19574     getAutoCreateNativeIOS : function()
19575     {
19576         var cfg = {
19577             cls: 'form-group' //input-group,
19578         };
19579         
19580         var combobox =  {
19581             tag: 'select',
19582             cls : 'roo-ios-select'
19583         };
19584         
19585         if (this.name) {
19586             combobox.name = this.name;
19587         }
19588         
19589         if (this.disabled) {
19590             combobox.disabled = true;
19591         }
19592         
19593         var settings = this;
19594         
19595         ['xs','sm','md','lg'].map(function(size){
19596             if (settings[size]) {
19597                 cfg.cls += ' col-' + size + '-' + settings[size];
19598             }
19599         });
19600         
19601         cfg.cn = combobox;
19602         
19603         return cfg;
19604         
19605     },
19606     
19607     initIOSView : function()
19608     {
19609         this.store.on('load', this.onIOSViewLoad, this);
19610         
19611         return;
19612     },
19613     
19614     onIOSViewLoad : function()
19615     {
19616         if(this.store.getCount() < 1){
19617             return;
19618         }
19619         
19620         this.clearIOSView();
19621         
19622         if(this.allowBlank) {
19623             
19624             var default_text = '-- SELECT --';
19625             
19626             if(this.placeholder.length){
19627                 default_text = this.placeholder;
19628             }
19629             
19630             if(this.emptyTitle.length){
19631                 default_text += ' - ' + this.emptyTitle + ' -';
19632             }
19633             
19634             var opt = this.inputEl().createChild({
19635                 tag: 'option',
19636                 value : 0,
19637                 html : default_text
19638             });
19639             
19640             var o = {};
19641             o[this.valueField] = 0;
19642             o[this.displayField] = default_text;
19643             
19644             this.ios_options.push({
19645                 data : o,
19646                 el : opt
19647             });
19648             
19649         }
19650         
19651         this.store.data.each(function(d, rowIndex){
19652             
19653             var html = '';
19654             
19655             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19656                 html = d.data[this.displayField];
19657             }
19658             
19659             var value = '';
19660             
19661             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19662                 value = d.data[this.valueField];
19663             }
19664             
19665             var option = {
19666                 tag: 'option',
19667                 value : value,
19668                 html : html
19669             };
19670             
19671             if(this.value == d.data[this.valueField]){
19672                 option['selected'] = true;
19673             }
19674             
19675             var opt = this.inputEl().createChild(option);
19676             
19677             this.ios_options.push({
19678                 data : d.data,
19679                 el : opt
19680             });
19681             
19682         }, this);
19683         
19684         this.inputEl().on('change', function(){
19685            this.fireEvent('select', this);
19686         }, this);
19687         
19688     },
19689     
19690     clearIOSView: function()
19691     {
19692         this.inputEl().dom.innerHTML = '';
19693         
19694         this.ios_options = [];
19695     },
19696     
19697     setIOSValue: function(v)
19698     {
19699         this.value = v;
19700         
19701         if(!this.ios_options){
19702             return;
19703         }
19704         
19705         Roo.each(this.ios_options, function(opts){
19706            
19707            opts.el.dom.removeAttribute('selected');
19708            
19709            if(opts.data[this.valueField] != v){
19710                return;
19711            }
19712            
19713            opts.el.dom.setAttribute('selected', true);
19714            
19715         }, this);
19716     }
19717
19718     /** 
19719     * @cfg {Boolean} grow 
19720     * @hide 
19721     */
19722     /** 
19723     * @cfg {Number} growMin 
19724     * @hide 
19725     */
19726     /** 
19727     * @cfg {Number} growMax 
19728     * @hide 
19729     */
19730     /**
19731      * @hide
19732      * @method autoSize
19733      */
19734 });
19735
19736 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19737     
19738     header : {
19739         tag: 'div',
19740         cls: 'modal-header',
19741         cn: [
19742             {
19743                 tag: 'h4',
19744                 cls: 'modal-title'
19745             }
19746         ]
19747     },
19748     
19749     body : {
19750         tag: 'div',
19751         cls: 'modal-body',
19752         cn: [
19753             {
19754                 tag: 'ul',
19755                 cls: 'list-group'
19756             }
19757         ]
19758     },
19759     
19760     listItemRadio : {
19761         tag: 'li',
19762         cls: 'list-group-item',
19763         cn: [
19764             {
19765                 tag: 'span',
19766                 cls: 'roo-combobox-list-group-item-value'
19767             },
19768             {
19769                 tag: 'div',
19770                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19771                 cn: [
19772                     {
19773                         tag: 'input',
19774                         type: 'radio'
19775                     },
19776                     {
19777                         tag: 'label'
19778                     }
19779                 ]
19780             }
19781         ]
19782     },
19783     
19784     listItemCheckbox : {
19785         tag: 'li',
19786         cls: 'list-group-item',
19787         cn: [
19788             {
19789                 tag: 'span',
19790                 cls: 'roo-combobox-list-group-item-value'
19791             },
19792             {
19793                 tag: 'div',
19794                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19795                 cn: [
19796                     {
19797                         tag: 'input',
19798                         type: 'checkbox'
19799                     },
19800                     {
19801                         tag: 'label'
19802                     }
19803                 ]
19804             }
19805         ]
19806     },
19807     
19808     emptyResult : {
19809         tag: 'div',
19810         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19811     },
19812     
19813     footer : {
19814         tag: 'div',
19815         cls: 'modal-footer',
19816         cn: [
19817             {
19818                 tag: 'div',
19819                 cls: 'row',
19820                 cn: [
19821                     {
19822                         tag: 'div',
19823                         cls: 'col-xs-6 text-left',
19824                         cn: {
19825                             tag: 'button',
19826                             cls: 'btn btn-danger roo-touch-view-cancel',
19827                             html: 'Cancel'
19828                         }
19829                     },
19830                     {
19831                         tag: 'div',
19832                         cls: 'col-xs-6 text-right',
19833                         cn: {
19834                             tag: 'button',
19835                             cls: 'btn btn-success roo-touch-view-ok',
19836                             html: 'OK'
19837                         }
19838                     }
19839                 ]
19840             }
19841         ]
19842         
19843     }
19844 });
19845
19846 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19847     
19848     touchViewTemplate : {
19849         tag: 'div',
19850         cls: 'modal fade roo-combobox-touch-view',
19851         cn: [
19852             {
19853                 tag: 'div',
19854                 cls: 'modal-dialog',
19855                 style : 'position:fixed', // we have to fix position....
19856                 cn: [
19857                     {
19858                         tag: 'div',
19859                         cls: 'modal-content',
19860                         cn: [
19861                             Roo.bootstrap.form.ComboBox.header,
19862                             Roo.bootstrap.form.ComboBox.body,
19863                             Roo.bootstrap.form.ComboBox.footer
19864                         ]
19865                     }
19866                 ]
19867             }
19868         ]
19869     }
19870 });/*
19871  * Based on:
19872  * Ext JS Library 1.1.1
19873  * Copyright(c) 2006-2007, Ext JS, LLC.
19874  *
19875  * Originally Released Under LGPL - original licence link has changed is not relivant.
19876  *
19877  * Fork - LGPL
19878  * <script type="text/javascript">
19879  */
19880
19881 /**
19882  * @class Roo.View
19883  * @extends Roo.util.Observable
19884  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19885  * This class also supports single and multi selection modes. <br>
19886  * Create a data model bound view:
19887  <pre><code>
19888  var store = new Roo.data.Store(...);
19889
19890  var view = new Roo.View({
19891     el : "my-element",
19892     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19893  
19894     singleSelect: true,
19895     selectedClass: "ydataview-selected",
19896     store: store
19897  });
19898
19899  // listen for node click?
19900  view.on("click", function(vw, index, node, e){
19901  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19902  });
19903
19904  // load XML data
19905  dataModel.load("foobar.xml");
19906  </code></pre>
19907  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19908  * <br><br>
19909  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19910  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19911  * 
19912  * Note: old style constructor is still suported (container, template, config)
19913  * 
19914  * @constructor
19915  * Create a new View
19916  * @param {Object} config The config object
19917  * 
19918  */
19919 Roo.View = function(config, depreciated_tpl, depreciated_config){
19920     
19921     this.parent = false;
19922     
19923     if (typeof(depreciated_tpl) == 'undefined') {
19924         // new way.. - universal constructor.
19925         Roo.apply(this, config);
19926         this.el  = Roo.get(this.el);
19927     } else {
19928         // old format..
19929         this.el  = Roo.get(config);
19930         this.tpl = depreciated_tpl;
19931         Roo.apply(this, depreciated_config);
19932     }
19933     this.wrapEl  = this.el.wrap().wrap();
19934     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19935     
19936     
19937     if(typeof(this.tpl) == "string"){
19938         this.tpl = new Roo.Template(this.tpl);
19939     } else {
19940         // support xtype ctors..
19941         this.tpl = new Roo.factory(this.tpl, Roo);
19942     }
19943     
19944     
19945     this.tpl.compile();
19946     
19947     /** @private */
19948     this.addEvents({
19949         /**
19950          * @event beforeclick
19951          * Fires before a click is processed. Returns false to cancel the default action.
19952          * @param {Roo.View} this
19953          * @param {Number} index The index of the target node
19954          * @param {HTMLElement} node The target node
19955          * @param {Roo.EventObject} e The raw event object
19956          */
19957             "beforeclick" : true,
19958         /**
19959          * @event click
19960          * Fires when a template node is clicked.
19961          * @param {Roo.View} this
19962          * @param {Number} index The index of the target node
19963          * @param {HTMLElement} node The target node
19964          * @param {Roo.EventObject} e The raw event object
19965          */
19966             "click" : true,
19967         /**
19968          * @event dblclick
19969          * Fires when a template node is double clicked.
19970          * @param {Roo.View} this
19971          * @param {Number} index The index of the target node
19972          * @param {HTMLElement} node The target node
19973          * @param {Roo.EventObject} e The raw event object
19974          */
19975             "dblclick" : true,
19976         /**
19977          * @event contextmenu
19978          * Fires when a template node is right clicked.
19979          * @param {Roo.View} this
19980          * @param {Number} index The index of the target node
19981          * @param {HTMLElement} node The target node
19982          * @param {Roo.EventObject} e The raw event object
19983          */
19984             "contextmenu" : true,
19985         /**
19986          * @event selectionchange
19987          * Fires when the selected nodes change.
19988          * @param {Roo.View} this
19989          * @param {Array} selections Array of the selected nodes
19990          */
19991             "selectionchange" : true,
19992     
19993         /**
19994          * @event beforeselect
19995          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19996          * @param {Roo.View} this
19997          * @param {HTMLElement} node The node to be selected
19998          * @param {Array} selections Array of currently selected nodes
19999          */
20000             "beforeselect" : true,
20001         /**
20002          * @event preparedata
20003          * Fires on every row to render, to allow you to change the data.
20004          * @param {Roo.View} this
20005          * @param {Object} data to be rendered (change this)
20006          */
20007           "preparedata" : true
20008           
20009           
20010         });
20011
20012
20013
20014     this.el.on({
20015         "click": this.onClick,
20016         "dblclick": this.onDblClick,
20017         "contextmenu": this.onContextMenu,
20018         scope:this
20019     });
20020
20021     this.selections = [];
20022     this.nodes = [];
20023     this.cmp = new Roo.CompositeElementLite([]);
20024     if(this.store){
20025         this.store = Roo.factory(this.store, Roo.data);
20026         this.setStore(this.store, true);
20027     }
20028     
20029     if ( this.footer && this.footer.xtype) {
20030            
20031          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20032         
20033         this.footer.dataSource = this.store;
20034         this.footer.container = fctr;
20035         this.footer = Roo.factory(this.footer, Roo);
20036         fctr.insertFirst(this.el);
20037         
20038         // this is a bit insane - as the paging toolbar seems to detach the el..
20039 //        dom.parentNode.parentNode.parentNode
20040          // they get detached?
20041     }
20042     
20043     
20044     Roo.View.superclass.constructor.call(this);
20045     
20046     
20047 };
20048
20049 Roo.extend(Roo.View, Roo.util.Observable, {
20050     
20051      /**
20052      * @cfg {Roo.data.Store} store Data store to load data from.
20053      */
20054     store : false,
20055     
20056     /**
20057      * @cfg {String|Roo.Element} el The container element.
20058      */
20059     el : '',
20060     
20061     /**
20062      * @cfg {String|Roo.Template} tpl The template used by this View 
20063      */
20064     tpl : false,
20065     /**
20066      * @cfg {String} dataName the named area of the template to use as the data area
20067      *                          Works with domtemplates roo-name="name"
20068      */
20069     dataName: false,
20070     /**
20071      * @cfg {String} selectedClass The css class to add to selected nodes
20072      */
20073     selectedClass : "x-view-selected",
20074      /**
20075      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20076      */
20077     emptyText : "",
20078     
20079     /**
20080      * @cfg {String} text to display on mask (default Loading)
20081      */
20082     mask : false,
20083     /**
20084      * @cfg {Boolean} multiSelect Allow multiple selection
20085      */
20086     multiSelect : false,
20087     /**
20088      * @cfg {Boolean} singleSelect Allow single selection
20089      */
20090     singleSelect:  false,
20091     
20092     /**
20093      * @cfg {Boolean} toggleSelect - selecting 
20094      */
20095     toggleSelect : false,
20096     
20097     /**
20098      * @cfg {Boolean} tickable - selecting 
20099      */
20100     tickable : false,
20101     
20102     /**
20103      * Returns the element this view is bound to.
20104      * @return {Roo.Element}
20105      */
20106     getEl : function(){
20107         return this.wrapEl;
20108     },
20109     
20110     
20111
20112     /**
20113      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20114      */
20115     refresh : function(){
20116         //Roo.log('refresh');
20117         var t = this.tpl;
20118         
20119         // if we are using something like 'domtemplate', then
20120         // the what gets used is:
20121         // t.applySubtemplate(NAME, data, wrapping data..)
20122         // the outer template then get' applied with
20123         //     the store 'extra data'
20124         // and the body get's added to the
20125         //      roo-name="data" node?
20126         //      <span class='roo-tpl-{name}'></span> ?????
20127         
20128         
20129         
20130         this.clearSelections();
20131         this.el.update("");
20132         var html = [];
20133         var records = this.store.getRange();
20134         if(records.length < 1) {
20135             
20136             // is this valid??  = should it render a template??
20137             
20138             this.el.update(this.emptyText);
20139             return;
20140         }
20141         var el = this.el;
20142         if (this.dataName) {
20143             this.el.update(t.apply(this.store.meta)); //????
20144             el = this.el.child('.roo-tpl-' + this.dataName);
20145         }
20146         
20147         for(var i = 0, len = records.length; i < len; i++){
20148             var data = this.prepareData(records[i].data, i, records[i]);
20149             this.fireEvent("preparedata", this, data, i, records[i]);
20150             
20151             var d = Roo.apply({}, data);
20152             
20153             if(this.tickable){
20154                 Roo.apply(d, {'roo-id' : Roo.id()});
20155                 
20156                 var _this = this;
20157             
20158                 Roo.each(this.parent.item, function(item){
20159                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20160                         return;
20161                     }
20162                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20163                 });
20164             }
20165             
20166             html[html.length] = Roo.util.Format.trim(
20167                 this.dataName ?
20168                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20169                     t.apply(d)
20170             );
20171         }
20172         
20173         
20174         
20175         el.update(html.join(""));
20176         this.nodes = el.dom.childNodes;
20177         this.updateIndexes(0);
20178     },
20179     
20180
20181     /**
20182      * Function to override to reformat the data that is sent to
20183      * the template for each node.
20184      * DEPRICATED - use the preparedata event handler.
20185      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20186      * a JSON object for an UpdateManager bound view).
20187      */
20188     prepareData : function(data, index, record)
20189     {
20190         this.fireEvent("preparedata", this, data, index, record);
20191         return data;
20192     },
20193
20194     onUpdate : function(ds, record){
20195         // Roo.log('on update');   
20196         this.clearSelections();
20197         var index = this.store.indexOf(record);
20198         var n = this.nodes[index];
20199         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20200         n.parentNode.removeChild(n);
20201         this.updateIndexes(index, index);
20202     },
20203
20204     
20205     
20206 // --------- FIXME     
20207     onAdd : function(ds, records, index)
20208     {
20209         //Roo.log(['on Add', ds, records, index] );        
20210         this.clearSelections();
20211         if(this.nodes.length == 0){
20212             this.refresh();
20213             return;
20214         }
20215         var n = this.nodes[index];
20216         for(var i = 0, len = records.length; i < len; i++){
20217             var d = this.prepareData(records[i].data, i, records[i]);
20218             if(n){
20219                 this.tpl.insertBefore(n, d);
20220             }else{
20221                 
20222                 this.tpl.append(this.el, d);
20223             }
20224         }
20225         this.updateIndexes(index);
20226     },
20227
20228     onRemove : function(ds, record, index){
20229        // Roo.log('onRemove');
20230         this.clearSelections();
20231         var el = this.dataName  ?
20232             this.el.child('.roo-tpl-' + this.dataName) :
20233             this.el; 
20234         
20235         el.dom.removeChild(this.nodes[index]);
20236         this.updateIndexes(index);
20237     },
20238
20239     /**
20240      * Refresh an individual node.
20241      * @param {Number} index
20242      */
20243     refreshNode : function(index){
20244         this.onUpdate(this.store, this.store.getAt(index));
20245     },
20246
20247     updateIndexes : function(startIndex, endIndex){
20248         var ns = this.nodes;
20249         startIndex = startIndex || 0;
20250         endIndex = endIndex || ns.length - 1;
20251         for(var i = startIndex; i <= endIndex; i++){
20252             ns[i].nodeIndex = i;
20253         }
20254     },
20255
20256     /**
20257      * Changes the data store this view uses and refresh the view.
20258      * @param {Store} store
20259      */
20260     setStore : function(store, initial){
20261         if(!initial && this.store){
20262             this.store.un("datachanged", this.refresh);
20263             this.store.un("add", this.onAdd);
20264             this.store.un("remove", this.onRemove);
20265             this.store.un("update", this.onUpdate);
20266             this.store.un("clear", this.refresh);
20267             this.store.un("beforeload", this.onBeforeLoad);
20268             this.store.un("load", this.onLoad);
20269             this.store.un("loadexception", this.onLoad);
20270         }
20271         if(store){
20272           
20273             store.on("datachanged", this.refresh, this);
20274             store.on("add", this.onAdd, this);
20275             store.on("remove", this.onRemove, this);
20276             store.on("update", this.onUpdate, this);
20277             store.on("clear", this.refresh, this);
20278             store.on("beforeload", this.onBeforeLoad, this);
20279             store.on("load", this.onLoad, this);
20280             store.on("loadexception", this.onLoad, this);
20281         }
20282         
20283         if(store){
20284             this.refresh();
20285         }
20286     },
20287     /**
20288      * onbeforeLoad - masks the loading area.
20289      *
20290      */
20291     onBeforeLoad : function(store,opts)
20292     {
20293          //Roo.log('onBeforeLoad');   
20294         if (!opts.add) {
20295             this.el.update("");
20296         }
20297         this.el.mask(this.mask ? this.mask : "Loading" ); 
20298     },
20299     onLoad : function ()
20300     {
20301         this.el.unmask();
20302     },
20303     
20304
20305     /**
20306      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20307      * @param {HTMLElement} node
20308      * @return {HTMLElement} The template node
20309      */
20310     findItemFromChild : function(node){
20311         var el = this.dataName  ?
20312             this.el.child('.roo-tpl-' + this.dataName,true) :
20313             this.el.dom; 
20314         
20315         if(!node || node.parentNode == el){
20316                     return node;
20317             }
20318             var p = node.parentNode;
20319             while(p && p != el){
20320             if(p.parentNode == el){
20321                 return p;
20322             }
20323             p = p.parentNode;
20324         }
20325             return null;
20326     },
20327
20328     /** @ignore */
20329     onClick : function(e){
20330         var item = this.findItemFromChild(e.getTarget());
20331         if(item){
20332             var index = this.indexOf(item);
20333             if(this.onItemClick(item, index, e) !== false){
20334                 this.fireEvent("click", this, index, item, e);
20335             }
20336         }else{
20337             this.clearSelections();
20338         }
20339     },
20340
20341     /** @ignore */
20342     onContextMenu : function(e){
20343         var item = this.findItemFromChild(e.getTarget());
20344         if(item){
20345             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20346         }
20347     },
20348
20349     /** @ignore */
20350     onDblClick : function(e){
20351         var item = this.findItemFromChild(e.getTarget());
20352         if(item){
20353             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20354         }
20355     },
20356
20357     onItemClick : function(item, index, e)
20358     {
20359         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20360             return false;
20361         }
20362         if (this.toggleSelect) {
20363             var m = this.isSelected(item) ? 'unselect' : 'select';
20364             //Roo.log(m);
20365             var _t = this;
20366             _t[m](item, true, false);
20367             return true;
20368         }
20369         if(this.multiSelect || this.singleSelect){
20370             if(this.multiSelect && e.shiftKey && this.lastSelection){
20371                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20372             }else{
20373                 this.select(item, this.multiSelect && e.ctrlKey);
20374                 this.lastSelection = item;
20375             }
20376             
20377             if(!this.tickable){
20378                 e.preventDefault();
20379             }
20380             
20381         }
20382         return true;
20383     },
20384
20385     /**
20386      * Get the number of selected nodes.
20387      * @return {Number}
20388      */
20389     getSelectionCount : function(){
20390         return this.selections.length;
20391     },
20392
20393     /**
20394      * Get the currently selected nodes.
20395      * @return {Array} An array of HTMLElements
20396      */
20397     getSelectedNodes : function(){
20398         return this.selections;
20399     },
20400
20401     /**
20402      * Get the indexes of the selected nodes.
20403      * @return {Array}
20404      */
20405     getSelectedIndexes : function(){
20406         var indexes = [], s = this.selections;
20407         for(var i = 0, len = s.length; i < len; i++){
20408             indexes.push(s[i].nodeIndex);
20409         }
20410         return indexes;
20411     },
20412
20413     /**
20414      * Clear all selections
20415      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20416      */
20417     clearSelections : function(suppressEvent){
20418         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20419             this.cmp.elements = this.selections;
20420             this.cmp.removeClass(this.selectedClass);
20421             this.selections = [];
20422             if(!suppressEvent){
20423                 this.fireEvent("selectionchange", this, this.selections);
20424             }
20425         }
20426     },
20427
20428     /**
20429      * Returns true if the passed node is selected
20430      * @param {HTMLElement/Number} node The node or node index
20431      * @return {Boolean}
20432      */
20433     isSelected : function(node){
20434         var s = this.selections;
20435         if(s.length < 1){
20436             return false;
20437         }
20438         node = this.getNode(node);
20439         return s.indexOf(node) !== -1;
20440     },
20441
20442     /**
20443      * Selects nodes.
20444      * @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
20445      * @param {Boolean} keepExisting (optional) true to keep existing selections
20446      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20447      */
20448     select : function(nodeInfo, keepExisting, suppressEvent){
20449         if(nodeInfo instanceof Array){
20450             if(!keepExisting){
20451                 this.clearSelections(true);
20452             }
20453             for(var i = 0, len = nodeInfo.length; i < len; i++){
20454                 this.select(nodeInfo[i], true, true);
20455             }
20456             return;
20457         } 
20458         var node = this.getNode(nodeInfo);
20459         if(!node || this.isSelected(node)){
20460             return; // already selected.
20461         }
20462         if(!keepExisting){
20463             this.clearSelections(true);
20464         }
20465         
20466         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20467             Roo.fly(node).addClass(this.selectedClass);
20468             this.selections.push(node);
20469             if(!suppressEvent){
20470                 this.fireEvent("selectionchange", this, this.selections);
20471             }
20472         }
20473         
20474         
20475     },
20476       /**
20477      * Unselects nodes.
20478      * @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
20479      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20480      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20481      */
20482     unselect : function(nodeInfo, keepExisting, suppressEvent)
20483     {
20484         if(nodeInfo instanceof Array){
20485             Roo.each(this.selections, function(s) {
20486                 this.unselect(s, nodeInfo);
20487             }, this);
20488             return;
20489         }
20490         var node = this.getNode(nodeInfo);
20491         if(!node || !this.isSelected(node)){
20492             //Roo.log("not selected");
20493             return; // not selected.
20494         }
20495         // fireevent???
20496         var ns = [];
20497         Roo.each(this.selections, function(s) {
20498             if (s == node ) {
20499                 Roo.fly(node).removeClass(this.selectedClass);
20500
20501                 return;
20502             }
20503             ns.push(s);
20504         },this);
20505         
20506         this.selections= ns;
20507         this.fireEvent("selectionchange", this, this.selections);
20508     },
20509
20510     /**
20511      * Gets a template node.
20512      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20513      * @return {HTMLElement} The node or null if it wasn't found
20514      */
20515     getNode : function(nodeInfo){
20516         if(typeof nodeInfo == "string"){
20517             return document.getElementById(nodeInfo);
20518         }else if(typeof nodeInfo == "number"){
20519             return this.nodes[nodeInfo];
20520         }
20521         return nodeInfo;
20522     },
20523
20524     /**
20525      * Gets a range template nodes.
20526      * @param {Number} startIndex
20527      * @param {Number} endIndex
20528      * @return {Array} An array of nodes
20529      */
20530     getNodes : function(start, end){
20531         var ns = this.nodes;
20532         start = start || 0;
20533         end = typeof end == "undefined" ? ns.length - 1 : end;
20534         var nodes = [];
20535         if(start <= end){
20536             for(var i = start; i <= end; i++){
20537                 nodes.push(ns[i]);
20538             }
20539         } else{
20540             for(var i = start; i >= end; i--){
20541                 nodes.push(ns[i]);
20542             }
20543         }
20544         return nodes;
20545     },
20546
20547     /**
20548      * Finds the index of the passed node
20549      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20550      * @return {Number} The index of the node or -1
20551      */
20552     indexOf : function(node){
20553         node = this.getNode(node);
20554         if(typeof node.nodeIndex == "number"){
20555             return node.nodeIndex;
20556         }
20557         var ns = this.nodes;
20558         for(var i = 0, len = ns.length; i < len; i++){
20559             if(ns[i] == node){
20560                 return i;
20561             }
20562         }
20563         return -1;
20564     }
20565 });
20566 /*
20567  * - LGPL
20568  *
20569  * based on jquery fullcalendar
20570  * 
20571  */
20572
20573 Roo.bootstrap = Roo.bootstrap || {};
20574 /**
20575  * @class Roo.bootstrap.Calendar
20576  * @extends Roo.bootstrap.Component
20577  * Bootstrap Calendar class
20578  * @cfg {Boolean} loadMask (true|false) default false
20579  * @cfg {Object} header generate the user specific header of the calendar, default false
20580
20581  * @constructor
20582  * Create a new Container
20583  * @param {Object} config The config object
20584  */
20585
20586
20587
20588 Roo.bootstrap.Calendar = function(config){
20589     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20590      this.addEvents({
20591         /**
20592              * @event select
20593              * Fires when a date is selected
20594              * @param {DatePicker} this
20595              * @param {Date} date The selected date
20596              */
20597         'select': true,
20598         /**
20599              * @event monthchange
20600              * Fires when the displayed month changes 
20601              * @param {DatePicker} this
20602              * @param {Date} date The selected month
20603              */
20604         'monthchange': true,
20605         /**
20606              * @event evententer
20607              * Fires when mouse over an event
20608              * @param {Calendar} this
20609              * @param {event} Event
20610              */
20611         'evententer': true,
20612         /**
20613              * @event eventleave
20614              * Fires when the mouse leaves an
20615              * @param {Calendar} this
20616              * @param {event}
20617              */
20618         'eventleave': true,
20619         /**
20620              * @event eventclick
20621              * Fires when the mouse click an
20622              * @param {Calendar} this
20623              * @param {event}
20624              */
20625         'eventclick': true
20626         
20627     });
20628
20629 };
20630
20631 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20632     
20633           /**
20634      * @cfg {Roo.data.Store} store
20635      * The data source for the calendar
20636      */
20637         store : false,
20638      /**
20639      * @cfg {Number} startDay
20640      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20641      */
20642     startDay : 0,
20643     
20644     loadMask : false,
20645     
20646     header : false,
20647       
20648     getAutoCreate : function(){
20649         
20650         
20651         var fc_button = function(name, corner, style, content ) {
20652             return Roo.apply({},{
20653                 tag : 'span',
20654                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20655                          (corner.length ?
20656                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20657                             ''
20658                         ),
20659                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20660                 unselectable: 'on'
20661             });
20662         };
20663         
20664         var header = {};
20665         
20666         if(!this.header){
20667             header = {
20668                 tag : 'table',
20669                 cls : 'fc-header',
20670                 style : 'width:100%',
20671                 cn : [
20672                     {
20673                         tag: 'tr',
20674                         cn : [
20675                             {
20676                                 tag : 'td',
20677                                 cls : 'fc-header-left',
20678                                 cn : [
20679                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20680                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20681                                     { tag: 'span', cls: 'fc-header-space' },
20682                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20683
20684
20685                                 ]
20686                             },
20687
20688                             {
20689                                 tag : 'td',
20690                                 cls : 'fc-header-center',
20691                                 cn : [
20692                                     {
20693                                         tag: 'span',
20694                                         cls: 'fc-header-title',
20695                                         cn : {
20696                                             tag: 'H2',
20697                                             html : 'month / year'
20698                                         }
20699                                     }
20700
20701                                 ]
20702                             },
20703                             {
20704                                 tag : 'td',
20705                                 cls : 'fc-header-right',
20706                                 cn : [
20707                               /*      fc_button('month', 'left', '', 'month' ),
20708                                     fc_button('week', '', '', 'week' ),
20709                                     fc_button('day', 'right', '', 'day' )
20710                                 */    
20711
20712                                 ]
20713                             }
20714
20715                         ]
20716                     }
20717                 ]
20718             };
20719         }
20720         
20721         header = this.header;
20722         
20723        
20724         var cal_heads = function() {
20725             var ret = [];
20726             // fixme - handle this.
20727             
20728             for (var i =0; i < Date.dayNames.length; i++) {
20729                 var d = Date.dayNames[i];
20730                 ret.push({
20731                     tag: 'th',
20732                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20733                     html : d.substring(0,3)
20734                 });
20735                 
20736             }
20737             ret[0].cls += ' fc-first';
20738             ret[6].cls += ' fc-last';
20739             return ret;
20740         };
20741         var cal_cell = function(n) {
20742             return  {
20743                 tag: 'td',
20744                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20745                 cn : [
20746                     {
20747                         cn : [
20748                             {
20749                                 cls: 'fc-day-number',
20750                                 html: 'D'
20751                             },
20752                             {
20753                                 cls: 'fc-day-content',
20754                              
20755                                 cn : [
20756                                      {
20757                                         style: 'position: relative;' // height: 17px;
20758                                     }
20759                                 ]
20760                             }
20761                             
20762                             
20763                         ]
20764                     }
20765                 ]
20766                 
20767             }
20768         };
20769         var cal_rows = function() {
20770             
20771             var ret = [];
20772             for (var r = 0; r < 6; r++) {
20773                 var row= {
20774                     tag : 'tr',
20775                     cls : 'fc-week',
20776                     cn : []
20777                 };
20778                 
20779                 for (var i =0; i < Date.dayNames.length; i++) {
20780                     var d = Date.dayNames[i];
20781                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20782
20783                 }
20784                 row.cn[0].cls+=' fc-first';
20785                 row.cn[0].cn[0].style = 'min-height:90px';
20786                 row.cn[6].cls+=' fc-last';
20787                 ret.push(row);
20788                 
20789             }
20790             ret[0].cls += ' fc-first';
20791             ret[4].cls += ' fc-prev-last';
20792             ret[5].cls += ' fc-last';
20793             return ret;
20794             
20795         };
20796         
20797         var cal_table = {
20798             tag: 'table',
20799             cls: 'fc-border-separate',
20800             style : 'width:100%',
20801             cellspacing  : 0,
20802             cn : [
20803                 { 
20804                     tag: 'thead',
20805                     cn : [
20806                         { 
20807                             tag: 'tr',
20808                             cls : 'fc-first fc-last',
20809                             cn : cal_heads()
20810                         }
20811                     ]
20812                 },
20813                 { 
20814                     tag: 'tbody',
20815                     cn : cal_rows()
20816                 }
20817                   
20818             ]
20819         };
20820          
20821          var cfg = {
20822             cls : 'fc fc-ltr',
20823             cn : [
20824                 header,
20825                 {
20826                     cls : 'fc-content',
20827                     style : "position: relative;",
20828                     cn : [
20829                         {
20830                             cls : 'fc-view fc-view-month fc-grid',
20831                             style : 'position: relative',
20832                             unselectable : 'on',
20833                             cn : [
20834                                 {
20835                                     cls : 'fc-event-container',
20836                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20837                                 },
20838                                 cal_table
20839                             ]
20840                         }
20841                     ]
20842     
20843                 }
20844            ] 
20845             
20846         };
20847         
20848          
20849         
20850         return cfg;
20851     },
20852     
20853     
20854     initEvents : function()
20855     {
20856         if(!this.store){
20857             throw "can not find store for calendar";
20858         }
20859         
20860         var mark = {
20861             tag: "div",
20862             cls:"x-dlg-mask",
20863             style: "text-align:center",
20864             cn: [
20865                 {
20866                     tag: "div",
20867                     style: "background-color:white;width:50%;margin:250 auto",
20868                     cn: [
20869                         {
20870                             tag: "img",
20871                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20872                         },
20873                         {
20874                             tag: "span",
20875                             html: "Loading"
20876                         }
20877                         
20878                     ]
20879                 }
20880             ]
20881         };
20882         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20883         
20884         var size = this.el.select('.fc-content', true).first().getSize();
20885         this.maskEl.setSize(size.width, size.height);
20886         this.maskEl.enableDisplayMode("block");
20887         if(!this.loadMask){
20888             this.maskEl.hide();
20889         }
20890         
20891         this.store = Roo.factory(this.store, Roo.data);
20892         this.store.on('load', this.onLoad, this);
20893         this.store.on('beforeload', this.onBeforeLoad, this);
20894         
20895         this.resize();
20896         
20897         this.cells = this.el.select('.fc-day',true);
20898         //Roo.log(this.cells);
20899         this.textNodes = this.el.query('.fc-day-number');
20900         this.cells.addClassOnOver('fc-state-hover');
20901         
20902         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20903         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20904         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20905         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20906         
20907         this.on('monthchange', this.onMonthChange, this);
20908         
20909         this.update(new Date().clearTime());
20910     },
20911     
20912     resize : function() {
20913         var sz  = this.el.getSize();
20914         
20915         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20916         this.el.select('.fc-day-content div',true).setHeight(34);
20917     },
20918     
20919     
20920     // private
20921     showPrevMonth : function(e){
20922         this.update(this.activeDate.add("mo", -1));
20923     },
20924     showToday : function(e){
20925         this.update(new Date().clearTime());
20926     },
20927     // private
20928     showNextMonth : function(e){
20929         this.update(this.activeDate.add("mo", 1));
20930     },
20931
20932     // private
20933     showPrevYear : function(){
20934         this.update(this.activeDate.add("y", -1));
20935     },
20936
20937     // private
20938     showNextYear : function(){
20939         this.update(this.activeDate.add("y", 1));
20940     },
20941
20942     
20943    // private
20944     update : function(date)
20945     {
20946         var vd = this.activeDate;
20947         this.activeDate = date;
20948 //        if(vd && this.el){
20949 //            var t = date.getTime();
20950 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20951 //                Roo.log('using add remove');
20952 //                
20953 //                this.fireEvent('monthchange', this, date);
20954 //                
20955 //                this.cells.removeClass("fc-state-highlight");
20956 //                this.cells.each(function(c){
20957 //                   if(c.dateValue == t){
20958 //                       c.addClass("fc-state-highlight");
20959 //                       setTimeout(function(){
20960 //                            try{c.dom.firstChild.focus();}catch(e){}
20961 //                       }, 50);
20962 //                       return false;
20963 //                   }
20964 //                   return true;
20965 //                });
20966 //                return;
20967 //            }
20968 //        }
20969         
20970         var days = date.getDaysInMonth();
20971         
20972         var firstOfMonth = date.getFirstDateOfMonth();
20973         var startingPos = firstOfMonth.getDay()-this.startDay;
20974         
20975         if(startingPos < this.startDay){
20976             startingPos += 7;
20977         }
20978         
20979         var pm = date.add(Date.MONTH, -1);
20980         var prevStart = pm.getDaysInMonth()-startingPos;
20981 //        
20982         this.cells = this.el.select('.fc-day',true);
20983         this.textNodes = this.el.query('.fc-day-number');
20984         this.cells.addClassOnOver('fc-state-hover');
20985         
20986         var cells = this.cells.elements;
20987         var textEls = this.textNodes;
20988         
20989         Roo.each(cells, function(cell){
20990             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20991         });
20992         
20993         days += startingPos;
20994
20995         // convert everything to numbers so it's fast
20996         var day = 86400000;
20997         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20998         //Roo.log(d);
20999         //Roo.log(pm);
21000         //Roo.log(prevStart);
21001         
21002         var today = new Date().clearTime().getTime();
21003         var sel = date.clearTime().getTime();
21004         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21005         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21006         var ddMatch = this.disabledDatesRE;
21007         var ddText = this.disabledDatesText;
21008         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21009         var ddaysText = this.disabledDaysText;
21010         var format = this.format;
21011         
21012         var setCellClass = function(cal, cell){
21013             cell.row = 0;
21014             cell.events = [];
21015             cell.more = [];
21016             //Roo.log('set Cell Class');
21017             cell.title = "";
21018             var t = d.getTime();
21019             
21020             //Roo.log(d);
21021             
21022             cell.dateValue = t;
21023             if(t == today){
21024                 cell.className += " fc-today";
21025                 cell.className += " fc-state-highlight";
21026                 cell.title = cal.todayText;
21027             }
21028             if(t == sel){
21029                 // disable highlight in other month..
21030                 //cell.className += " fc-state-highlight";
21031                 
21032             }
21033             // disabling
21034             if(t < min) {
21035                 cell.className = " fc-state-disabled";
21036                 cell.title = cal.minText;
21037                 return;
21038             }
21039             if(t > max) {
21040                 cell.className = " fc-state-disabled";
21041                 cell.title = cal.maxText;
21042                 return;
21043             }
21044             if(ddays){
21045                 if(ddays.indexOf(d.getDay()) != -1){
21046                     cell.title = ddaysText;
21047                     cell.className = " fc-state-disabled";
21048                 }
21049             }
21050             if(ddMatch && format){
21051                 var fvalue = d.dateFormat(format);
21052                 if(ddMatch.test(fvalue)){
21053                     cell.title = ddText.replace("%0", fvalue);
21054                     cell.className = " fc-state-disabled";
21055                 }
21056             }
21057             
21058             if (!cell.initialClassName) {
21059                 cell.initialClassName = cell.dom.className;
21060             }
21061             
21062             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21063         };
21064
21065         var i = 0;
21066         
21067         for(; i < startingPos; i++) {
21068             textEls[i].innerHTML = (++prevStart);
21069             d.setDate(d.getDate()+1);
21070             
21071             cells[i].className = "fc-past fc-other-month";
21072             setCellClass(this, cells[i]);
21073         }
21074         
21075         var intDay = 0;
21076         
21077         for(; i < days; i++){
21078             intDay = i - startingPos + 1;
21079             textEls[i].innerHTML = (intDay);
21080             d.setDate(d.getDate()+1);
21081             
21082             cells[i].className = ''; // "x-date-active";
21083             setCellClass(this, cells[i]);
21084         }
21085         var extraDays = 0;
21086         
21087         for(; i < 42; i++) {
21088             textEls[i].innerHTML = (++extraDays);
21089             d.setDate(d.getDate()+1);
21090             
21091             cells[i].className = "fc-future fc-other-month";
21092             setCellClass(this, cells[i]);
21093         }
21094         
21095         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21096         
21097         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21098         
21099         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21100         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21101         
21102         if(totalRows != 6){
21103             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21104             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21105         }
21106         
21107         this.fireEvent('monthchange', this, date);
21108         
21109         
21110         /*
21111         if(!this.internalRender){
21112             var main = this.el.dom.firstChild;
21113             var w = main.offsetWidth;
21114             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21115             Roo.fly(main).setWidth(w);
21116             this.internalRender = true;
21117             // opera does not respect the auto grow header center column
21118             // then, after it gets a width opera refuses to recalculate
21119             // without a second pass
21120             if(Roo.isOpera && !this.secondPass){
21121                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21122                 this.secondPass = true;
21123                 this.update.defer(10, this, [date]);
21124             }
21125         }
21126         */
21127         
21128     },
21129     
21130     findCell : function(dt) {
21131         dt = dt.clearTime().getTime();
21132         var ret = false;
21133         this.cells.each(function(c){
21134             //Roo.log("check " +c.dateValue + '?=' + dt);
21135             if(c.dateValue == dt){
21136                 ret = c;
21137                 return false;
21138             }
21139             return true;
21140         });
21141         
21142         return ret;
21143     },
21144     
21145     findCells : function(ev) {
21146         var s = ev.start.clone().clearTime().getTime();
21147        // Roo.log(s);
21148         var e= ev.end.clone().clearTime().getTime();
21149        // Roo.log(e);
21150         var ret = [];
21151         this.cells.each(function(c){
21152              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21153             
21154             if(c.dateValue > e){
21155                 return ;
21156             }
21157             if(c.dateValue < s){
21158                 return ;
21159             }
21160             ret.push(c);
21161         });
21162         
21163         return ret;    
21164     },
21165     
21166 //    findBestRow: function(cells)
21167 //    {
21168 //        var ret = 0;
21169 //        
21170 //        for (var i =0 ; i < cells.length;i++) {
21171 //            ret  = Math.max(cells[i].rows || 0,ret);
21172 //        }
21173 //        return ret;
21174 //        
21175 //    },
21176     
21177     
21178     addItem : function(ev)
21179     {
21180         // look for vertical location slot in
21181         var cells = this.findCells(ev);
21182         
21183 //        ev.row = this.findBestRow(cells);
21184         
21185         // work out the location.
21186         
21187         var crow = false;
21188         var rows = [];
21189         for(var i =0; i < cells.length; i++) {
21190             
21191             cells[i].row = cells[0].row;
21192             
21193             if(i == 0){
21194                 cells[i].row = cells[i].row + 1;
21195             }
21196             
21197             if (!crow) {
21198                 crow = {
21199                     start : cells[i],
21200                     end :  cells[i]
21201                 };
21202                 continue;
21203             }
21204             if (crow.start.getY() == cells[i].getY()) {
21205                 // on same row.
21206                 crow.end = cells[i];
21207                 continue;
21208             }
21209             // different row.
21210             rows.push(crow);
21211             crow = {
21212                 start: cells[i],
21213                 end : cells[i]
21214             };
21215             
21216         }
21217         
21218         rows.push(crow);
21219         ev.els = [];
21220         ev.rows = rows;
21221         ev.cells = cells;
21222         
21223         cells[0].events.push(ev);
21224         
21225         this.calevents.push(ev);
21226     },
21227     
21228     clearEvents: function() {
21229         
21230         if(!this.calevents){
21231             return;
21232         }
21233         
21234         Roo.each(this.cells.elements, function(c){
21235             c.row = 0;
21236             c.events = [];
21237             c.more = [];
21238         });
21239         
21240         Roo.each(this.calevents, function(e) {
21241             Roo.each(e.els, function(el) {
21242                 el.un('mouseenter' ,this.onEventEnter, this);
21243                 el.un('mouseleave' ,this.onEventLeave, this);
21244                 el.remove();
21245             },this);
21246         },this);
21247         
21248         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21249             e.remove();
21250         });
21251         
21252     },
21253     
21254     renderEvents: function()
21255     {   
21256         var _this = this;
21257         
21258         this.cells.each(function(c) {
21259             
21260             if(c.row < 5){
21261                 return;
21262             }
21263             
21264             var ev = c.events;
21265             
21266             var r = 4;
21267             if(c.row != c.events.length){
21268                 r = 4 - (4 - (c.row - c.events.length));
21269             }
21270             
21271             c.events = ev.slice(0, r);
21272             c.more = ev.slice(r);
21273             
21274             if(c.more.length && c.more.length == 1){
21275                 c.events.push(c.more.pop());
21276             }
21277             
21278             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21279             
21280         });
21281             
21282         this.cells.each(function(c) {
21283             
21284             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21285             
21286             
21287             for (var e = 0; e < c.events.length; e++){
21288                 var ev = c.events[e];
21289                 var rows = ev.rows;
21290                 
21291                 for(var i = 0; i < rows.length; i++) {
21292                 
21293                     // how many rows should it span..
21294
21295                     var  cfg = {
21296                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21297                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21298
21299                         unselectable : "on",
21300                         cn : [
21301                             {
21302                                 cls: 'fc-event-inner',
21303                                 cn : [
21304     //                                {
21305     //                                  tag:'span',
21306     //                                  cls: 'fc-event-time',
21307     //                                  html : cells.length > 1 ? '' : ev.time
21308     //                                },
21309                                     {
21310                                       tag:'span',
21311                                       cls: 'fc-event-title',
21312                                       html : String.format('{0}', ev.title)
21313                                     }
21314
21315
21316                                 ]
21317                             },
21318                             {
21319                                 cls: 'ui-resizable-handle ui-resizable-e',
21320                                 html : '&nbsp;&nbsp;&nbsp'
21321                             }
21322
21323                         ]
21324                     };
21325
21326                     if (i == 0) {
21327                         cfg.cls += ' fc-event-start';
21328                     }
21329                     if ((i+1) == rows.length) {
21330                         cfg.cls += ' fc-event-end';
21331                     }
21332
21333                     var ctr = _this.el.select('.fc-event-container',true).first();
21334                     var cg = ctr.createChild(cfg);
21335
21336                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21337                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21338
21339                     var r = (c.more.length) ? 1 : 0;
21340                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21341                     cg.setWidth(ebox.right - sbox.x -2);
21342
21343                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21344                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21345                     cg.on('click', _this.onEventClick, _this, ev);
21346
21347                     ev.els.push(cg);
21348                     
21349                 }
21350                 
21351             }
21352             
21353             
21354             if(c.more.length){
21355                 var  cfg = {
21356                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21357                     style : 'position: absolute',
21358                     unselectable : "on",
21359                     cn : [
21360                         {
21361                             cls: 'fc-event-inner',
21362                             cn : [
21363                                 {
21364                                   tag:'span',
21365                                   cls: 'fc-event-title',
21366                                   html : 'More'
21367                                 }
21368
21369
21370                             ]
21371                         },
21372                         {
21373                             cls: 'ui-resizable-handle ui-resizable-e',
21374                             html : '&nbsp;&nbsp;&nbsp'
21375                         }
21376
21377                     ]
21378                 };
21379
21380                 var ctr = _this.el.select('.fc-event-container',true).first();
21381                 var cg = ctr.createChild(cfg);
21382
21383                 var sbox = c.select('.fc-day-content',true).first().getBox();
21384                 var ebox = c.select('.fc-day-content',true).first().getBox();
21385                 //Roo.log(cg);
21386                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21387                 cg.setWidth(ebox.right - sbox.x -2);
21388
21389                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21390                 
21391             }
21392             
21393         });
21394         
21395         
21396         
21397     },
21398     
21399     onEventEnter: function (e, el,event,d) {
21400         this.fireEvent('evententer', this, el, event);
21401     },
21402     
21403     onEventLeave: function (e, el,event,d) {
21404         this.fireEvent('eventleave', this, el, event);
21405     },
21406     
21407     onEventClick: function (e, el,event,d) {
21408         this.fireEvent('eventclick', this, el, event);
21409     },
21410     
21411     onMonthChange: function () {
21412         this.store.load();
21413     },
21414     
21415     onMoreEventClick: function(e, el, more)
21416     {
21417         var _this = this;
21418         
21419         this.calpopover.placement = 'right';
21420         this.calpopover.setTitle('More');
21421         
21422         this.calpopover.setContent('');
21423         
21424         var ctr = this.calpopover.el.select('.popover-content', true).first();
21425         
21426         Roo.each(more, function(m){
21427             var cfg = {
21428                 cls : 'fc-event-hori fc-event-draggable',
21429                 html : m.title
21430             };
21431             var cg = ctr.createChild(cfg);
21432             
21433             cg.on('click', _this.onEventClick, _this, m);
21434         });
21435         
21436         this.calpopover.show(el);
21437         
21438         
21439     },
21440     
21441     onLoad: function () 
21442     {   
21443         this.calevents = [];
21444         var cal = this;
21445         
21446         if(this.store.getCount() > 0){
21447             this.store.data.each(function(d){
21448                cal.addItem({
21449                     id : d.data.id,
21450                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21451                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21452                     time : d.data.start_time,
21453                     title : d.data.title,
21454                     description : d.data.description,
21455                     venue : d.data.venue
21456                 });
21457             });
21458         }
21459         
21460         this.renderEvents();
21461         
21462         if(this.calevents.length && this.loadMask){
21463             this.maskEl.hide();
21464         }
21465     },
21466     
21467     onBeforeLoad: function()
21468     {
21469         this.clearEvents();
21470         if(this.loadMask){
21471             this.maskEl.show();
21472         }
21473     }
21474 });
21475
21476  
21477  /*
21478  * - LGPL
21479  *
21480  * element
21481  * 
21482  */
21483
21484 /**
21485  * @class Roo.bootstrap.Popover
21486  * @extends Roo.bootstrap.Component
21487  * @builder-top
21488  * @children Roo.bootstrap.Component
21489  * Bootstrap Popover class
21490  * @cfg {String} html contents of the popover   (or false to use children..)
21491  * @cfg {String} title of popover (or false to hide)
21492  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21493  * @cfg {String} trigger click || hover (or false to trigger manually)
21494  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21495  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21496  *      - if false and it has a 'parent' then it will be automatically added to that element
21497  *      - if string - Roo.get  will be called 
21498  * @cfg {Number} delay - delay before showing
21499  
21500  * @constructor
21501  * Create a new Popover
21502  * @param {Object} config The config object
21503  */
21504
21505 Roo.bootstrap.Popover = function(config){
21506     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21507     
21508     this.addEvents({
21509         // raw events
21510          /**
21511          * @event show
21512          * After the popover show
21513          * 
21514          * @param {Roo.bootstrap.Popover} this
21515          */
21516         "show" : true,
21517         /**
21518          * @event hide
21519          * After the popover hide
21520          * 
21521          * @param {Roo.bootstrap.Popover} this
21522          */
21523         "hide" : true
21524     });
21525 };
21526
21527 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21528     
21529     title: false,
21530     html: false,
21531     
21532     placement : 'right',
21533     trigger : 'hover', // hover
21534     modal : false,
21535     delay : 0,
21536     
21537     over: false,
21538     
21539     can_build_overlaid : false,
21540     
21541     maskEl : false, // the mask element
21542     headerEl : false,
21543     contentEl : false,
21544     alignEl : false, // when show is called with an element - this get's stored.
21545     
21546     getChildContainer : function()
21547     {
21548         return this.contentEl;
21549         
21550     },
21551     getPopoverHeader : function()
21552     {
21553         this.title = true; // flag not to hide it..
21554         this.headerEl.addClass('p-0');
21555         return this.headerEl
21556     },
21557     
21558     
21559     getAutoCreate : function(){
21560          
21561         var cfg = {
21562            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21563            style: 'display:block',
21564            cn : [
21565                 {
21566                     cls : 'arrow'
21567                 },
21568                 {
21569                     cls : 'popover-inner ',
21570                     cn : [
21571                         {
21572                             tag: 'h3',
21573                             cls: 'popover-title popover-header',
21574                             html : this.title === false ? '' : this.title
21575                         },
21576                         {
21577                             cls : 'popover-content popover-body '  + (this.cls || ''),
21578                             html : this.html || ''
21579                         }
21580                     ]
21581                     
21582                 }
21583            ]
21584         };
21585         
21586         return cfg;
21587     },
21588     /**
21589      * @param {string} the title
21590      */
21591     setTitle: function(str)
21592     {
21593         this.title = str;
21594         if (this.el) {
21595             this.headerEl.dom.innerHTML = str;
21596         }
21597         
21598     },
21599     /**
21600      * @param {string} the body content
21601      */
21602     setContent: function(str)
21603     {
21604         this.html = str;
21605         if (this.contentEl) {
21606             this.contentEl.dom.innerHTML = str;
21607         }
21608         
21609     },
21610     // as it get's added to the bottom of the page.
21611     onRender : function(ct, position)
21612     {
21613         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21614         
21615         
21616         
21617         if(!this.el){
21618             var cfg = Roo.apply({},  this.getAutoCreate());
21619             cfg.id = Roo.id();
21620             
21621             if (this.cls) {
21622                 cfg.cls += ' ' + this.cls;
21623             }
21624             if (this.style) {
21625                 cfg.style = this.style;
21626             }
21627             //Roo.log("adding to ");
21628             this.el = Roo.get(document.body).createChild(cfg, position);
21629 //            Roo.log(this.el);
21630         }
21631         
21632         this.contentEl = this.el.select('.popover-content',true).first();
21633         this.headerEl =  this.el.select('.popover-title',true).first();
21634         
21635         var nitems = [];
21636         if(typeof(this.items) != 'undefined'){
21637             var items = this.items;
21638             delete this.items;
21639
21640             for(var i =0;i < items.length;i++) {
21641                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21642             }
21643         }
21644
21645         this.items = nitems;
21646         
21647         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21648         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21649         
21650         
21651         
21652         this.initEvents();
21653     },
21654     
21655     resizeMask : function()
21656     {
21657         this.maskEl.setSize(
21658             Roo.lib.Dom.getViewWidth(true),
21659             Roo.lib.Dom.getViewHeight(true)
21660         );
21661     },
21662     
21663     initEvents : function()
21664     {
21665         
21666         if (!this.modal) { 
21667             Roo.bootstrap.Popover.register(this);
21668         }
21669          
21670         this.arrowEl = this.el.select('.arrow',true).first();
21671         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21672         this.el.enableDisplayMode('block');
21673         this.el.hide();
21674  
21675         
21676         if (this.over === false && !this.parent()) {
21677             return; 
21678         }
21679         if (this.triggers === false) {
21680             return;
21681         }
21682          
21683         // support parent
21684         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21685         var triggers = this.trigger ? this.trigger.split(' ') : [];
21686         Roo.each(triggers, function(trigger) {
21687         
21688             if (trigger == 'click') {
21689                 on_el.on('click', this.toggle, this);
21690             } else if (trigger != 'manual') {
21691                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21692                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21693       
21694                 on_el.on(eventIn  ,this.enter, this);
21695                 on_el.on(eventOut, this.leave, this);
21696             }
21697         }, this);
21698     },
21699     
21700     
21701     // private
21702     timeout : null,
21703     hoverState : null,
21704     
21705     toggle : function () {
21706         this.hoverState == 'in' ? this.leave() : this.enter();
21707     },
21708     
21709     enter : function () {
21710         
21711         clearTimeout(this.timeout);
21712     
21713         this.hoverState = 'in';
21714     
21715         if (!this.delay || !this.delay.show) {
21716             this.show();
21717             return;
21718         }
21719         var _t = this;
21720         this.timeout = setTimeout(function () {
21721             if (_t.hoverState == 'in') {
21722                 _t.show();
21723             }
21724         }, this.delay.show)
21725     },
21726     
21727     leave : function() {
21728         clearTimeout(this.timeout);
21729     
21730         this.hoverState = 'out';
21731     
21732         if (!this.delay || !this.delay.hide) {
21733             this.hide();
21734             return;
21735         }
21736         var _t = this;
21737         this.timeout = setTimeout(function () {
21738             if (_t.hoverState == 'out') {
21739                 _t.hide();
21740             }
21741         }, this.delay.hide)
21742     },
21743     /**
21744      * Show the popover
21745      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21746      * @param {string} (left|right|top|bottom) position
21747      */
21748     show : function (on_el, placement)
21749     {
21750         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21751         on_el = on_el || false; // default to false
21752          
21753         if (!on_el) {
21754             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21755                 on_el = this.parent().el;
21756             } else if (this.over) {
21757                 on_el = Roo.get(this.over);
21758             }
21759             
21760         }
21761         
21762         this.alignEl = Roo.get( on_el );
21763
21764         if (!this.el) {
21765             this.render(document.body);
21766         }
21767         
21768         
21769          
21770         
21771         if (this.title === false) {
21772             this.headerEl.hide();
21773         }
21774         
21775        
21776         this.el.show();
21777         this.el.dom.style.display = 'block';
21778          
21779  
21780         if (this.alignEl) {
21781             this.updatePosition(this.placement, true);
21782              
21783         } else {
21784             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21785             var es = this.el.getSize();
21786             var x = Roo.lib.Dom.getViewWidth()/2;
21787             var y = Roo.lib.Dom.getViewHeight()/2;
21788             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21789             
21790         }
21791
21792         
21793         //var arrow = this.el.select('.arrow',true).first();
21794         //arrow.set(align[2], 
21795         
21796         this.el.addClass('in');
21797         
21798          
21799         
21800         this.hoverState = 'in';
21801         
21802         if (this.modal) {
21803             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21804             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21805             this.maskEl.dom.style.display = 'block';
21806             this.maskEl.addClass('show');
21807         }
21808         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21809  
21810         this.fireEvent('show', this);
21811         
21812     },
21813     /**
21814      * fire this manually after loading a grid in the table for example
21815      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21816      * @param {Boolean} try and move it if we cant get right position.
21817      */
21818     updatePosition : function(placement, try_move)
21819     {
21820         // allow for calling with no parameters
21821         placement = placement   ? placement :  this.placement;
21822         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21823         
21824         this.el.removeClass([
21825             'fade','top','bottom', 'left', 'right','in',
21826             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21827         ]);
21828         this.el.addClass(placement + ' bs-popover-' + placement);
21829         
21830         if (!this.alignEl ) {
21831             return false;
21832         }
21833         
21834         switch (placement) {
21835             case 'right':
21836                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21837                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21838                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21839                     //normal display... or moved up/down.
21840                     this.el.setXY(offset);
21841                     var xy = this.alignEl.getAnchorXY('tr', false);
21842                     xy[0]+=2;xy[1]+=5;
21843                     this.arrowEl.setXY(xy);
21844                     return true;
21845                 }
21846                 // continue through...
21847                 return this.updatePosition('left', false);
21848                 
21849             
21850             case 'left':
21851                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21852                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21853                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21854                     //normal display... or moved up/down.
21855                     this.el.setXY(offset);
21856                     var xy = this.alignEl.getAnchorXY('tl', false);
21857                     xy[0]-=10;xy[1]+=5; // << fix me
21858                     this.arrowEl.setXY(xy);
21859                     return true;
21860                 }
21861                 // call self...
21862                 return this.updatePosition('right', false);
21863             
21864             case 'top':
21865                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21866                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21867                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21868                     //normal display... or moved up/down.
21869                     this.el.setXY(offset);
21870                     var xy = this.alignEl.getAnchorXY('t', false);
21871                     xy[1]-=10; // << fix me
21872                     this.arrowEl.setXY(xy);
21873                     return true;
21874                 }
21875                 // fall through
21876                return this.updatePosition('bottom', false);
21877             
21878             case 'bottom':
21879                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21880                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21881                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21882                     //normal display... or moved up/down.
21883                     this.el.setXY(offset);
21884                     var xy = this.alignEl.getAnchorXY('b', false);
21885                      xy[1]+=2; // << fix me
21886                     this.arrowEl.setXY(xy);
21887                     return true;
21888                 }
21889                 // fall through
21890                 return this.updatePosition('top', false);
21891                 
21892             
21893         }
21894         
21895         
21896         return false;
21897     },
21898     
21899     hide : function()
21900     {
21901         this.el.setXY([0,0]);
21902         this.el.removeClass('in');
21903         this.el.hide();
21904         this.hoverState = null;
21905         this.maskEl.hide(); // always..
21906         this.fireEvent('hide', this);
21907     }
21908     
21909 });
21910
21911
21912 Roo.apply(Roo.bootstrap.Popover, {
21913
21914     alignment : {
21915         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21916         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21917         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21918         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21919     },
21920     
21921     zIndex : 20001,
21922
21923     clickHander : false,
21924     
21925     
21926
21927     onMouseDown : function(e)
21928     {
21929         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21930             /// what is nothing is showing..
21931             this.hideAll();
21932         }
21933          
21934     },
21935     
21936     
21937     popups : [],
21938     
21939     register : function(popup)
21940     {
21941         if (!Roo.bootstrap.Popover.clickHandler) {
21942             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21943         }
21944         // hide other popups.
21945         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21946         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21947         this.hideAll(); //<< why?
21948         //this.popups.push(popup);
21949     },
21950     hideAll : function()
21951     {
21952         this.popups.forEach(function(p) {
21953             p.hide();
21954         });
21955     },
21956     onShow : function() {
21957         Roo.bootstrap.Popover.popups.push(this);
21958     },
21959     onHide : function() {
21960         Roo.bootstrap.Popover.popups.remove(this);
21961     } 
21962
21963 });/*
21964  * - LGPL
21965  *
21966  * Card header - holder for the card header elements.
21967  * 
21968  */
21969
21970 /**
21971  * @class Roo.bootstrap.PopoverNav
21972  * @extends Roo.bootstrap.nav.Simplebar
21973  * @parent Roo.bootstrap.Popover
21974  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21975  * Bootstrap Popover header navigation class
21976  * FIXME? should this go under nav?
21977  *
21978  * 
21979  * @constructor
21980  * Create a new Popover Header Navigation 
21981  * @param {Object} config The config object
21982  */
21983
21984 Roo.bootstrap.PopoverNav = function(config){
21985     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21986 };
21987
21988 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
21989     
21990     
21991     container_method : 'getPopoverHeader' 
21992     
21993      
21994     
21995     
21996    
21997 });
21998
21999  
22000
22001  /*
22002  * - LGPL
22003  *
22004  * Progress
22005  * 
22006  */
22007
22008 /**
22009  * @class Roo.bootstrap.Progress
22010  * @extends Roo.bootstrap.Component
22011  * @children Roo.bootstrap.ProgressBar
22012  * Bootstrap Progress class
22013  * @cfg {Boolean} striped striped of the progress bar
22014  * @cfg {Boolean} active animated of the progress bar
22015  * 
22016  * 
22017  * @constructor
22018  * Create a new Progress
22019  * @param {Object} config The config object
22020  */
22021
22022 Roo.bootstrap.Progress = function(config){
22023     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22024 };
22025
22026 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22027     
22028     striped : false,
22029     active: false,
22030     
22031     getAutoCreate : function(){
22032         var cfg = {
22033             tag: 'div',
22034             cls: 'progress'
22035         };
22036         
22037         
22038         if(this.striped){
22039             cfg.cls += ' progress-striped';
22040         }
22041       
22042         if(this.active){
22043             cfg.cls += ' active';
22044         }
22045         
22046         
22047         return cfg;
22048     }
22049    
22050 });
22051
22052  
22053
22054  /*
22055  * - LGPL
22056  *
22057  * ProgressBar
22058  * 
22059  */
22060
22061 /**
22062  * @class Roo.bootstrap.ProgressBar
22063  * @extends Roo.bootstrap.Component
22064  * Bootstrap ProgressBar class
22065  * @cfg {Number} aria_valuenow aria-value now
22066  * @cfg {Number} aria_valuemin aria-value min
22067  * @cfg {Number} aria_valuemax aria-value max
22068  * @cfg {String} label label for the progress bar
22069  * @cfg {String} panel (success | info | warning | danger )
22070  * @cfg {String} role role of the progress bar
22071  * @cfg {String} sr_only text
22072  * 
22073  * 
22074  * @constructor
22075  * Create a new ProgressBar
22076  * @param {Object} config The config object
22077  */
22078
22079 Roo.bootstrap.ProgressBar = function(config){
22080     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22081 };
22082
22083 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22084     
22085     aria_valuenow : 0,
22086     aria_valuemin : 0,
22087     aria_valuemax : 100,
22088     label : false,
22089     panel : false,
22090     role : false,
22091     sr_only: false,
22092     
22093     getAutoCreate : function()
22094     {
22095         
22096         var cfg = {
22097             tag: 'div',
22098             cls: 'progress-bar',
22099             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22100         };
22101         
22102         if(this.sr_only){
22103             cfg.cn = {
22104                 tag: 'span',
22105                 cls: 'sr-only',
22106                 html: this.sr_only
22107             }
22108         }
22109         
22110         if(this.role){
22111             cfg.role = this.role;
22112         }
22113         
22114         if(this.aria_valuenow){
22115             cfg['aria-valuenow'] = this.aria_valuenow;
22116         }
22117         
22118         if(this.aria_valuemin){
22119             cfg['aria-valuemin'] = this.aria_valuemin;
22120         }
22121         
22122         if(this.aria_valuemax){
22123             cfg['aria-valuemax'] = this.aria_valuemax;
22124         }
22125         
22126         if(this.label && !this.sr_only){
22127             cfg.html = this.label;
22128         }
22129         
22130         if(this.panel){
22131             cfg.cls += ' progress-bar-' + this.panel;
22132         }
22133         
22134         return cfg;
22135     },
22136     
22137     update : function(aria_valuenow)
22138     {
22139         this.aria_valuenow = aria_valuenow;
22140         
22141         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22142     }
22143    
22144 });
22145
22146  
22147
22148  /**
22149  * @class Roo.bootstrap.TabGroup
22150  * @extends Roo.bootstrap.Column
22151  * @children Roo.bootstrap.TabPanel
22152  * Bootstrap Column class
22153  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22154  * @cfg {Boolean} carousel true to make the group behave like a carousel
22155  * @cfg {Boolean} bullets show bullets for the panels
22156  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22157  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22158  * @cfg {Boolean} showarrow (true|false) show arrow default true
22159  * 
22160  * @constructor
22161  * Create a new TabGroup
22162  * @param {Object} config The config object
22163  */
22164
22165 Roo.bootstrap.TabGroup = function(config){
22166     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22167     if (!this.navId) {
22168         this.navId = Roo.id();
22169     }
22170     this.tabs = [];
22171     Roo.bootstrap.TabGroup.register(this);
22172     
22173 };
22174
22175 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22176     
22177     carousel : false,
22178     transition : false,
22179     bullets : 0,
22180     timer : 0,
22181     autoslide : false,
22182     slideFn : false,
22183     slideOnTouch : false,
22184     showarrow : true,
22185     
22186     getAutoCreate : function()
22187     {
22188         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22189         
22190         cfg.cls += ' tab-content';
22191         
22192         if (this.carousel) {
22193             cfg.cls += ' carousel slide';
22194             
22195             cfg.cn = [{
22196                cls : 'carousel-inner',
22197                cn : []
22198             }];
22199         
22200             if(this.bullets  && !Roo.isTouch){
22201                 
22202                 var bullets = {
22203                     cls : 'carousel-bullets',
22204                     cn : []
22205                 };
22206                
22207                 if(this.bullets_cls){
22208                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22209                 }
22210                 
22211                 bullets.cn.push({
22212                     cls : 'clear'
22213                 });
22214                 
22215                 cfg.cn[0].cn.push(bullets);
22216             }
22217             
22218             if(this.showarrow){
22219                 cfg.cn[0].cn.push({
22220                     tag : 'div',
22221                     class : 'carousel-arrow',
22222                     cn : [
22223                         {
22224                             tag : 'div',
22225                             class : 'carousel-prev',
22226                             cn : [
22227                                 {
22228                                     tag : 'i',
22229                                     class : 'fa fa-chevron-left'
22230                                 }
22231                             ]
22232                         },
22233                         {
22234                             tag : 'div',
22235                             class : 'carousel-next',
22236                             cn : [
22237                                 {
22238                                     tag : 'i',
22239                                     class : 'fa fa-chevron-right'
22240                                 }
22241                             ]
22242                         }
22243                     ]
22244                 });
22245             }
22246             
22247         }
22248         
22249         return cfg;
22250     },
22251     
22252     initEvents:  function()
22253     {
22254 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22255 //            this.el.on("touchstart", this.onTouchStart, this);
22256 //        }
22257         
22258         if(this.autoslide){
22259             var _this = this;
22260             
22261             this.slideFn = window.setInterval(function() {
22262                 _this.showPanelNext();
22263             }, this.timer);
22264         }
22265         
22266         if(this.showarrow){
22267             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22268             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22269         }
22270         
22271         
22272     },
22273     
22274 //    onTouchStart : function(e, el, o)
22275 //    {
22276 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22277 //            return;
22278 //        }
22279 //        
22280 //        this.showPanelNext();
22281 //    },
22282     
22283     
22284     getChildContainer : function()
22285     {
22286         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22287     },
22288     
22289     /**
22290     * register a Navigation item
22291     * @param {Roo.bootstrap.nav.Item} the navitem to add
22292     */
22293     register : function(item)
22294     {
22295         this.tabs.push( item);
22296         item.navId = this.navId; // not really needed..
22297         this.addBullet();
22298     
22299     },
22300     
22301     getActivePanel : function()
22302     {
22303         var r = false;
22304         Roo.each(this.tabs, function(t) {
22305             if (t.active) {
22306                 r = t;
22307                 return false;
22308             }
22309             return null;
22310         });
22311         return r;
22312         
22313     },
22314     getPanelByName : function(n)
22315     {
22316         var r = false;
22317         Roo.each(this.tabs, function(t) {
22318             if (t.tabId == n) {
22319                 r = t;
22320                 return false;
22321             }
22322             return null;
22323         });
22324         return r;
22325     },
22326     indexOfPanel : function(p)
22327     {
22328         var r = false;
22329         Roo.each(this.tabs, function(t,i) {
22330             if (t.tabId == p.tabId) {
22331                 r = i;
22332                 return false;
22333             }
22334             return null;
22335         });
22336         return r;
22337     },
22338     /**
22339      * show a specific panel
22340      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22341      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22342      */
22343     showPanel : function (pan)
22344     {
22345         if(this.transition || typeof(pan) == 'undefined'){
22346             Roo.log("waiting for the transitionend");
22347             return false;
22348         }
22349         
22350         if (typeof(pan) == 'number') {
22351             pan = this.tabs[pan];
22352         }
22353         
22354         if (typeof(pan) == 'string') {
22355             pan = this.getPanelByName(pan);
22356         }
22357         
22358         var cur = this.getActivePanel();
22359         
22360         if(!pan || !cur){
22361             Roo.log('pan or acitve pan is undefined');
22362             return false;
22363         }
22364         
22365         if (pan.tabId == this.getActivePanel().tabId) {
22366             return true;
22367         }
22368         
22369         if (false === cur.fireEvent('beforedeactivate')) {
22370             return false;
22371         }
22372         
22373         if(this.bullets > 0 && !Roo.isTouch){
22374             this.setActiveBullet(this.indexOfPanel(pan));
22375         }
22376         
22377         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22378             
22379             //class="carousel-item carousel-item-next carousel-item-left"
22380             
22381             this.transition = true;
22382             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22383             var lr = dir == 'next' ? 'left' : 'right';
22384             pan.el.addClass(dir); // or prev
22385             pan.el.addClass('carousel-item-' + dir); // or prev
22386             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22387             cur.el.addClass(lr); // or right
22388             pan.el.addClass(lr);
22389             cur.el.addClass('carousel-item-' +lr); // or right
22390             pan.el.addClass('carousel-item-' +lr);
22391             
22392             
22393             var _this = this;
22394             cur.el.on('transitionend', function() {
22395                 Roo.log("trans end?");
22396                 
22397                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22398                 pan.setActive(true);
22399                 
22400                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22401                 cur.setActive(false);
22402                 
22403                 _this.transition = false;
22404                 
22405             }, this, { single:  true } );
22406             
22407             return true;
22408         }
22409         
22410         cur.setActive(false);
22411         pan.setActive(true);
22412         
22413         return true;
22414         
22415     },
22416     showPanelNext : function()
22417     {
22418         var i = this.indexOfPanel(this.getActivePanel());
22419         
22420         if (i >= this.tabs.length - 1 && !this.autoslide) {
22421             return;
22422         }
22423         
22424         if (i >= this.tabs.length - 1 && this.autoslide) {
22425             i = -1;
22426         }
22427         
22428         this.showPanel(this.tabs[i+1]);
22429     },
22430     
22431     showPanelPrev : function()
22432     {
22433         var i = this.indexOfPanel(this.getActivePanel());
22434         
22435         if (i  < 1 && !this.autoslide) {
22436             return;
22437         }
22438         
22439         if (i < 1 && this.autoslide) {
22440             i = this.tabs.length;
22441         }
22442         
22443         this.showPanel(this.tabs[i-1]);
22444     },
22445     
22446     
22447     addBullet: function()
22448     {
22449         if(!this.bullets || Roo.isTouch){
22450             return;
22451         }
22452         var ctr = this.el.select('.carousel-bullets',true).first();
22453         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22454         var bullet = ctr.createChild({
22455             cls : 'bullet bullet-' + i
22456         },ctr.dom.lastChild);
22457         
22458         
22459         var _this = this;
22460         
22461         bullet.on('click', (function(e, el, o, ii, t){
22462
22463             e.preventDefault();
22464
22465             this.showPanel(ii);
22466
22467             if(this.autoslide && this.slideFn){
22468                 clearInterval(this.slideFn);
22469                 this.slideFn = window.setInterval(function() {
22470                     _this.showPanelNext();
22471                 }, this.timer);
22472             }
22473
22474         }).createDelegate(this, [i, bullet], true));
22475                 
22476         
22477     },
22478      
22479     setActiveBullet : function(i)
22480     {
22481         if(Roo.isTouch){
22482             return;
22483         }
22484         
22485         Roo.each(this.el.select('.bullet', true).elements, function(el){
22486             el.removeClass('selected');
22487         });
22488
22489         var bullet = this.el.select('.bullet-' + i, true).first();
22490         
22491         if(!bullet){
22492             return;
22493         }
22494         
22495         bullet.addClass('selected');
22496     }
22497     
22498     
22499   
22500 });
22501
22502  
22503
22504  
22505  
22506 Roo.apply(Roo.bootstrap.TabGroup, {
22507     
22508     groups: {},
22509      /**
22510     * register a Navigation Group
22511     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22512     */
22513     register : function(navgrp)
22514     {
22515         this.groups[navgrp.navId] = navgrp;
22516         
22517     },
22518     /**
22519     * fetch a Navigation Group based on the navigation ID
22520     * if one does not exist , it will get created.
22521     * @param {string} the navgroup to add
22522     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22523     */
22524     get: function(navId) {
22525         if (typeof(this.groups[navId]) == 'undefined') {
22526             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22527         }
22528         return this.groups[navId] ;
22529     }
22530     
22531     
22532     
22533 });
22534
22535  /*
22536  * - LGPL
22537  *
22538  * TabPanel
22539  * 
22540  */
22541
22542 /**
22543  * @class Roo.bootstrap.TabPanel
22544  * @extends Roo.bootstrap.Component
22545  * @children Roo.bootstrap.Component
22546  * Bootstrap TabPanel class
22547  * @cfg {Boolean} active panel active
22548  * @cfg {String} html panel content
22549  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22550  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22551  * @cfg {String} href click to link..
22552  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22553  * 
22554  * 
22555  * @constructor
22556  * Create a new TabPanel
22557  * @param {Object} config The config object
22558  */
22559
22560 Roo.bootstrap.TabPanel = function(config){
22561     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22562     this.addEvents({
22563         /**
22564              * @event changed
22565              * Fires when the active status changes
22566              * @param {Roo.bootstrap.TabPanel} this
22567              * @param {Boolean} state the new state
22568             
22569          */
22570         'changed': true,
22571         /**
22572              * @event beforedeactivate
22573              * Fires before a tab is de-activated - can be used to do validation on a form.
22574              * @param {Roo.bootstrap.TabPanel} this
22575              * @return {Boolean} false if there is an error
22576             
22577          */
22578         'beforedeactivate': true
22579      });
22580     
22581     this.tabId = this.tabId || Roo.id();
22582   
22583 };
22584
22585 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22586     
22587     active: false,
22588     html: false,
22589     tabId: false,
22590     navId : false,
22591     href : '',
22592     touchSlide : false,
22593     getAutoCreate : function(){
22594         
22595         
22596         var cfg = {
22597             tag: 'div',
22598             // item is needed for carousel - not sure if it has any effect otherwise
22599             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22600             html: this.html || ''
22601         };
22602         
22603         if(this.active){
22604             cfg.cls += ' active';
22605         }
22606         
22607         if(this.tabId){
22608             cfg.tabId = this.tabId;
22609         }
22610         
22611         
22612         
22613         return cfg;
22614     },
22615     
22616     initEvents:  function()
22617     {
22618         var p = this.parent();
22619         
22620         this.navId = this.navId || p.navId;
22621         
22622         if (typeof(this.navId) != 'undefined') {
22623             // not really needed.. but just in case.. parent should be a NavGroup.
22624             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22625             
22626             tg.register(this);
22627             
22628             var i = tg.tabs.length - 1;
22629             
22630             if(this.active && tg.bullets > 0 && i < tg.bullets){
22631                 tg.setActiveBullet(i);
22632             }
22633         }
22634         
22635         this.el.on('click', this.onClick, this);
22636         
22637         if(Roo.isTouch && this.touchSlide){
22638             this.el.on("touchstart", this.onTouchStart, this);
22639             this.el.on("touchmove", this.onTouchMove, this);
22640             this.el.on("touchend", this.onTouchEnd, this);
22641         }
22642         
22643     },
22644     
22645     onRender : function(ct, position)
22646     {
22647         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22648     },
22649     
22650     setActive : function(state)
22651     {
22652         Roo.log("panel - set active " + this.tabId + "=" + state);
22653         
22654         this.active = state;
22655         if (!state) {
22656             this.el.removeClass('active');
22657             
22658         } else  if (!this.el.hasClass('active')) {
22659             this.el.addClass('active');
22660         }
22661         
22662         this.fireEvent('changed', this, state);
22663     },
22664     
22665     onClick : function(e)
22666     {
22667         e.preventDefault();
22668         
22669         if(!this.href.length){
22670             return;
22671         }
22672         
22673         window.location.href = this.href;
22674     },
22675     
22676     startX : 0,
22677     startY : 0,
22678     endX : 0,
22679     endY : 0,
22680     swiping : false,
22681     
22682     onTouchStart : function(e)
22683     {
22684         this.swiping = false;
22685         
22686         this.startX = e.browserEvent.touches[0].clientX;
22687         this.startY = e.browserEvent.touches[0].clientY;
22688     },
22689     
22690     onTouchMove : function(e)
22691     {
22692         this.swiping = true;
22693         
22694         this.endX = e.browserEvent.touches[0].clientX;
22695         this.endY = e.browserEvent.touches[0].clientY;
22696     },
22697     
22698     onTouchEnd : function(e)
22699     {
22700         if(!this.swiping){
22701             this.onClick(e);
22702             return;
22703         }
22704         
22705         var tabGroup = this.parent();
22706         
22707         if(this.endX > this.startX){ // swiping right
22708             tabGroup.showPanelPrev();
22709             return;
22710         }
22711         
22712         if(this.startX > this.endX){ // swiping left
22713             tabGroup.showPanelNext();
22714             return;
22715         }
22716     }
22717     
22718     
22719 });
22720  
22721
22722  
22723
22724  /*
22725  * - LGPL
22726  *
22727  * DateField
22728  * 
22729  */
22730
22731 /**
22732  * @class Roo.bootstrap.form.DateField
22733  * @extends Roo.bootstrap.form.Input
22734  * Bootstrap DateField class
22735  * @cfg {Number} weekStart default 0
22736  * @cfg {String} viewMode default empty, (months|years)
22737  * @cfg {String} minViewMode default empty, (months|years)
22738  * @cfg {Number} startDate default -Infinity
22739  * @cfg {Number} endDate default Infinity
22740  * @cfg {Boolean} todayHighlight default false
22741  * @cfg {Boolean} todayBtn default false
22742  * @cfg {Boolean} calendarWeeks default false
22743  * @cfg {Object} daysOfWeekDisabled default empty
22744  * @cfg {Boolean} singleMode default false (true | false)
22745  * 
22746  * @cfg {Boolean} keyboardNavigation default true
22747  * @cfg {String} language default en
22748  * 
22749  * @constructor
22750  * Create a new DateField
22751  * @param {Object} config The config object
22752  */
22753
22754 Roo.bootstrap.form.DateField = function(config){
22755     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22756      this.addEvents({
22757             /**
22758              * @event show
22759              * Fires when this field show.
22760              * @param {Roo.bootstrap.form.DateField} this
22761              * @param {Mixed} date The date value
22762              */
22763             show : true,
22764             /**
22765              * @event show
22766              * Fires when this field hide.
22767              * @param {Roo.bootstrap.form.DateField} this
22768              * @param {Mixed} date The date value
22769              */
22770             hide : true,
22771             /**
22772              * @event select
22773              * Fires when select a date.
22774              * @param {Roo.bootstrap.form.DateField} this
22775              * @param {Mixed} date The date value
22776              */
22777             select : true,
22778             /**
22779              * @event beforeselect
22780              * Fires when before select a date.
22781              * @param {Roo.bootstrap.form.DateField} this
22782              * @param {Mixed} date The date value
22783              */
22784             beforeselect : true
22785         });
22786 };
22787
22788 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22789     
22790     /**
22791      * @cfg {String} format
22792      * The default date format string which can be overriden for localization support.  The format must be
22793      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22794      */
22795     format : "m/d/y",
22796     /**
22797      * @cfg {String} altFormats
22798      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22799      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22800      */
22801     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22802     
22803     weekStart : 0,
22804     
22805     viewMode : '',
22806     
22807     minViewMode : '',
22808     
22809     todayHighlight : false,
22810     
22811     todayBtn: false,
22812     
22813     language: 'en',
22814     
22815     keyboardNavigation: true,
22816     
22817     calendarWeeks: false,
22818     
22819     startDate: -Infinity,
22820     
22821     endDate: Infinity,
22822     
22823     daysOfWeekDisabled: [],
22824     
22825     _events: [],
22826     
22827     singleMode : false,
22828     
22829     UTCDate: function()
22830     {
22831         return new Date(Date.UTC.apply(Date, arguments));
22832     },
22833     
22834     UTCToday: function()
22835     {
22836         var today = new Date();
22837         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22838     },
22839     
22840     getDate: function() {
22841             var d = this.getUTCDate();
22842             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22843     },
22844     
22845     getUTCDate: function() {
22846             return this.date;
22847     },
22848     
22849     setDate: function(d) {
22850             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22851     },
22852     
22853     setUTCDate: function(d) {
22854             this.date = d;
22855             this.setValue(this.formatDate(this.date));
22856     },
22857         
22858     onRender: function(ct, position)
22859     {
22860         
22861         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22862         
22863         this.language = this.language || 'en';
22864         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22865         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22866         
22867         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22868         this.format = this.format || 'm/d/y';
22869         this.isInline = false;
22870         this.isInput = true;
22871         this.component = this.el.select('.add-on', true).first() || false;
22872         this.component = (this.component && this.component.length === 0) ? false : this.component;
22873         this.hasInput = this.component && this.inputEl().length;
22874         
22875         if (typeof(this.minViewMode === 'string')) {
22876             switch (this.minViewMode) {
22877                 case 'months':
22878                     this.minViewMode = 1;
22879                     break;
22880                 case 'years':
22881                     this.minViewMode = 2;
22882                     break;
22883                 default:
22884                     this.minViewMode = 0;
22885                     break;
22886             }
22887         }
22888         
22889         if (typeof(this.viewMode === 'string')) {
22890             switch (this.viewMode) {
22891                 case 'months':
22892                     this.viewMode = 1;
22893                     break;
22894                 case 'years':
22895                     this.viewMode = 2;
22896                     break;
22897                 default:
22898                     this.viewMode = 0;
22899                     break;
22900             }
22901         }
22902                 
22903         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22904         
22905 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22906         
22907         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22908         
22909         this.picker().on('mousedown', this.onMousedown, this);
22910         this.picker().on('click', this.onClick, this);
22911         
22912         this.picker().addClass('datepicker-dropdown');
22913         
22914         this.startViewMode = this.viewMode;
22915         
22916         if(this.singleMode){
22917             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22918                 v.setVisibilityMode(Roo.Element.DISPLAY);
22919                 v.hide();
22920             });
22921             
22922             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22923                 v.setStyle('width', '189px');
22924             });
22925         }
22926         
22927         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22928             if(!this.calendarWeeks){
22929                 v.remove();
22930                 return;
22931             }
22932             
22933             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22934             v.attr('colspan', function(i, val){
22935                 return parseInt(val) + 1;
22936             });
22937         });
22938                         
22939         
22940         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22941         
22942         this.setStartDate(this.startDate);
22943         this.setEndDate(this.endDate);
22944         
22945         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22946         
22947         this.fillDow();
22948         this.fillMonths();
22949         this.update();
22950         this.showMode();
22951         
22952         if(this.isInline) {
22953             this.showPopup();
22954         }
22955     },
22956     
22957     picker : function()
22958     {
22959         return this.pickerEl;
22960 //        return this.el.select('.datepicker', true).first();
22961     },
22962     
22963     fillDow: function()
22964     {
22965         var dowCnt = this.weekStart;
22966         
22967         var dow = {
22968             tag: 'tr',
22969             cn: [
22970                 
22971             ]
22972         };
22973         
22974         if(this.calendarWeeks){
22975             dow.cn.push({
22976                 tag: 'th',
22977                 cls: 'cw',
22978                 html: '&nbsp;'
22979             })
22980         }
22981         
22982         while (dowCnt < this.weekStart + 7) {
22983             dow.cn.push({
22984                 tag: 'th',
22985                 cls: 'dow',
22986                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22987             });
22988         }
22989         
22990         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22991     },
22992     
22993     fillMonths: function()
22994     {    
22995         var i = 0;
22996         var months = this.picker().select('>.datepicker-months td', true).first();
22997         
22998         months.dom.innerHTML = '';
22999         
23000         while (i < 12) {
23001             var month = {
23002                 tag: 'span',
23003                 cls: 'month',
23004                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23005             };
23006             
23007             months.createChild(month);
23008         }
23009         
23010     },
23011     
23012     update: function()
23013     {
23014         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;
23015         
23016         if (this.date < this.startDate) {
23017             this.viewDate = new Date(this.startDate);
23018         } else if (this.date > this.endDate) {
23019             this.viewDate = new Date(this.endDate);
23020         } else {
23021             this.viewDate = new Date(this.date);
23022         }
23023         
23024         this.fill();
23025     },
23026     
23027     fill: function() 
23028     {
23029         var d = new Date(this.viewDate),
23030                 year = d.getUTCFullYear(),
23031                 month = d.getUTCMonth(),
23032                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23033                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23034                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23035                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23036                 currentDate = this.date && this.date.valueOf(),
23037                 today = this.UTCToday();
23038         
23039         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23040         
23041 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23042         
23043 //        this.picker.select('>tfoot th.today').
23044 //                                              .text(dates[this.language].today)
23045 //                                              .toggle(this.todayBtn !== false);
23046     
23047         this.updateNavArrows();
23048         this.fillMonths();
23049                                                 
23050         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23051         
23052         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23053          
23054         prevMonth.setUTCDate(day);
23055         
23056         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23057         
23058         var nextMonth = new Date(prevMonth);
23059         
23060         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23061         
23062         nextMonth = nextMonth.valueOf();
23063         
23064         var fillMonths = false;
23065         
23066         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23067         
23068         while(prevMonth.valueOf() <= nextMonth) {
23069             var clsName = '';
23070             
23071             if (prevMonth.getUTCDay() === this.weekStart) {
23072                 if(fillMonths){
23073                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23074                 }
23075                     
23076                 fillMonths = {
23077                     tag: 'tr',
23078                     cn: []
23079                 };
23080                 
23081                 if(this.calendarWeeks){
23082                     // ISO 8601: First week contains first thursday.
23083                     // ISO also states week starts on Monday, but we can be more abstract here.
23084                     var
23085                     // Start of current week: based on weekstart/current date
23086                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23087                     // Thursday of this week
23088                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23089                     // First Thursday of year, year from thursday
23090                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23091                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23092                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23093                     
23094                     fillMonths.cn.push({
23095                         tag: 'td',
23096                         cls: 'cw',
23097                         html: calWeek
23098                     });
23099                 }
23100             }
23101             
23102             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23103                 clsName += ' old';
23104             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23105                 clsName += ' new';
23106             }
23107             if (this.todayHighlight &&
23108                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23109                 prevMonth.getUTCMonth() == today.getMonth() &&
23110                 prevMonth.getUTCDate() == today.getDate()) {
23111                 clsName += ' today';
23112             }
23113             
23114             if (currentDate && prevMonth.valueOf() === currentDate) {
23115                 clsName += ' active';
23116             }
23117             
23118             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23119                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23120                     clsName += ' disabled';
23121             }
23122             
23123             fillMonths.cn.push({
23124                 tag: 'td',
23125                 cls: 'day ' + clsName,
23126                 html: prevMonth.getDate()
23127             });
23128             
23129             prevMonth.setDate(prevMonth.getDate()+1);
23130         }
23131           
23132         var currentYear = this.date && this.date.getUTCFullYear();
23133         var currentMonth = this.date && this.date.getUTCMonth();
23134         
23135         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23136         
23137         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23138             v.removeClass('active');
23139             
23140             if(currentYear === year && k === currentMonth){
23141                 v.addClass('active');
23142             }
23143             
23144             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23145                 v.addClass('disabled');
23146             }
23147             
23148         });
23149         
23150         
23151         year = parseInt(year/10, 10) * 10;
23152         
23153         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23154         
23155         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23156         
23157         year -= 1;
23158         for (var i = -1; i < 11; i++) {
23159             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23160                 tag: 'span',
23161                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23162                 html: year
23163             });
23164             
23165             year += 1;
23166         }
23167     },
23168     
23169     showMode: function(dir) 
23170     {
23171         if (dir) {
23172             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23173         }
23174         
23175         Roo.each(this.picker().select('>div',true).elements, function(v){
23176             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23177             v.hide();
23178         });
23179         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23180     },
23181     
23182     place: function()
23183     {
23184         if(this.isInline) {
23185             return;
23186         }
23187         
23188         this.picker().removeClass(['bottom', 'top']);
23189         
23190         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23191             /*
23192              * place to the top of element!
23193              *
23194              */
23195             
23196             this.picker().addClass('top');
23197             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23198             
23199             return;
23200         }
23201         
23202         this.picker().addClass('bottom');
23203         
23204         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23205     },
23206     
23207     parseDate : function(value)
23208     {
23209         if(!value || value instanceof Date){
23210             return value;
23211         }
23212         var v = Date.parseDate(value, this.format);
23213         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23214             v = Date.parseDate(value, 'Y-m-d');
23215         }
23216         if(!v && this.altFormats){
23217             if(!this.altFormatsArray){
23218                 this.altFormatsArray = this.altFormats.split("|");
23219             }
23220             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23221                 v = Date.parseDate(value, this.altFormatsArray[i]);
23222             }
23223         }
23224         return v;
23225     },
23226     
23227     formatDate : function(date, fmt)
23228     {   
23229         return (!date || !(date instanceof Date)) ?
23230         date : date.dateFormat(fmt || this.format);
23231     },
23232     
23233     onFocus : function()
23234     {
23235         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23236         this.showPopup();
23237     },
23238     
23239     onBlur : function()
23240     {
23241         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23242         
23243         var d = this.inputEl().getValue();
23244         
23245         this.setValue(d);
23246                 
23247         this.hidePopup();
23248     },
23249     
23250     showPopup : function()
23251     {
23252         this.picker().show();
23253         this.update();
23254         this.place();
23255         
23256         this.fireEvent('showpopup', this, this.date);
23257     },
23258     
23259     hidePopup : function()
23260     {
23261         if(this.isInline) {
23262             return;
23263         }
23264         this.picker().hide();
23265         this.viewMode = this.startViewMode;
23266         this.showMode();
23267         
23268         this.fireEvent('hidepopup', this, this.date);
23269         
23270     },
23271     
23272     onMousedown: function(e)
23273     {
23274         e.stopPropagation();
23275         e.preventDefault();
23276     },
23277     
23278     keyup: function(e)
23279     {
23280         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23281         this.update();
23282     },
23283
23284     setValue: function(v)
23285     {
23286         if(this.fireEvent('beforeselect', this, v) !== false){
23287             var d = new Date(this.parseDate(v) ).clearTime();
23288         
23289             if(isNaN(d.getTime())){
23290                 this.date = this.viewDate = '';
23291                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23292                 return;
23293             }
23294
23295             v = this.formatDate(d);
23296
23297             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23298
23299             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23300
23301             this.update();
23302
23303             this.fireEvent('select', this, this.date);
23304         }
23305     },
23306     
23307     getValue: function()
23308     {
23309         return this.formatDate(this.date);
23310     },
23311     
23312     fireKey: function(e)
23313     {
23314         if (!this.picker().isVisible()){
23315             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23316                 this.showPopup();
23317             }
23318             return;
23319         }
23320         
23321         var dateChanged = false,
23322         dir, day, month,
23323         newDate, newViewDate;
23324         
23325         switch(e.keyCode){
23326             case 27: // escape
23327                 this.hidePopup();
23328                 e.preventDefault();
23329                 break;
23330             case 37: // left
23331             case 39: // right
23332                 if (!this.keyboardNavigation) {
23333                     break;
23334                 }
23335                 dir = e.keyCode == 37 ? -1 : 1;
23336                 
23337                 if (e.ctrlKey){
23338                     newDate = this.moveYear(this.date, dir);
23339                     newViewDate = this.moveYear(this.viewDate, dir);
23340                 } else if (e.shiftKey){
23341                     newDate = this.moveMonth(this.date, dir);
23342                     newViewDate = this.moveMonth(this.viewDate, dir);
23343                 } else {
23344                     newDate = new Date(this.date);
23345                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23346                     newViewDate = new Date(this.viewDate);
23347                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23348                 }
23349                 if (this.dateWithinRange(newDate)){
23350                     this.date = newDate;
23351                     this.viewDate = newViewDate;
23352                     this.setValue(this.formatDate(this.date));
23353 //                    this.update();
23354                     e.preventDefault();
23355                     dateChanged = true;
23356                 }
23357                 break;
23358             case 38: // up
23359             case 40: // down
23360                 if (!this.keyboardNavigation) {
23361                     break;
23362                 }
23363                 dir = e.keyCode == 38 ? -1 : 1;
23364                 if (e.ctrlKey){
23365                     newDate = this.moveYear(this.date, dir);
23366                     newViewDate = this.moveYear(this.viewDate, dir);
23367                 } else if (e.shiftKey){
23368                     newDate = this.moveMonth(this.date, dir);
23369                     newViewDate = this.moveMonth(this.viewDate, dir);
23370                 } else {
23371                     newDate = new Date(this.date);
23372                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23373                     newViewDate = new Date(this.viewDate);
23374                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23375                 }
23376                 if (this.dateWithinRange(newDate)){
23377                     this.date = newDate;
23378                     this.viewDate = newViewDate;
23379                     this.setValue(this.formatDate(this.date));
23380 //                    this.update();
23381                     e.preventDefault();
23382                     dateChanged = true;
23383                 }
23384                 break;
23385             case 13: // enter
23386                 this.setValue(this.formatDate(this.date));
23387                 this.hidePopup();
23388                 e.preventDefault();
23389                 break;
23390             case 9: // tab
23391                 this.setValue(this.formatDate(this.date));
23392                 this.hidePopup();
23393                 break;
23394             case 16: // shift
23395             case 17: // ctrl
23396             case 18: // alt
23397                 break;
23398             default :
23399                 this.hidePopup();
23400                 
23401         }
23402     },
23403     
23404     
23405     onClick: function(e) 
23406     {
23407         e.stopPropagation();
23408         e.preventDefault();
23409         
23410         var target = e.getTarget();
23411         
23412         if(target.nodeName.toLowerCase() === 'i'){
23413             target = Roo.get(target).dom.parentNode;
23414         }
23415         
23416         var nodeName = target.nodeName;
23417         var className = target.className;
23418         var html = target.innerHTML;
23419         //Roo.log(nodeName);
23420         
23421         switch(nodeName.toLowerCase()) {
23422             case 'th':
23423                 switch(className) {
23424                     case 'switch':
23425                         this.showMode(1);
23426                         break;
23427                     case 'prev':
23428                     case 'next':
23429                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23430                         switch(this.viewMode){
23431                                 case 0:
23432                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23433                                         break;
23434                                 case 1:
23435                                 case 2:
23436                                         this.viewDate = this.moveYear(this.viewDate, dir);
23437                                         break;
23438                         }
23439                         this.fill();
23440                         break;
23441                     case 'today':
23442                         var date = new Date();
23443                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23444 //                        this.fill()
23445                         this.setValue(this.formatDate(this.date));
23446                         
23447                         this.hidePopup();
23448                         break;
23449                 }
23450                 break;
23451             case 'span':
23452                 if (className.indexOf('disabled') < 0) {
23453                 if (!this.viewDate) {
23454                     this.viewDate = new Date();
23455                 }
23456                 this.viewDate.setUTCDate(1);
23457                     if (className.indexOf('month') > -1) {
23458                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23459                     } else {
23460                         var year = parseInt(html, 10) || 0;
23461                         this.viewDate.setUTCFullYear(year);
23462                         
23463                     }
23464                     
23465                     if(this.singleMode){
23466                         this.setValue(this.formatDate(this.viewDate));
23467                         this.hidePopup();
23468                         return;
23469                     }
23470                     
23471                     this.showMode(-1);
23472                     this.fill();
23473                 }
23474                 break;
23475                 
23476             case 'td':
23477                 //Roo.log(className);
23478                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23479                     var day = parseInt(html, 10) || 1;
23480                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23481                         month = (this.viewDate || new Date()).getUTCMonth();
23482
23483                     if (className.indexOf('old') > -1) {
23484                         if(month === 0 ){
23485                             month = 11;
23486                             year -= 1;
23487                         }else{
23488                             month -= 1;
23489                         }
23490                     } else if (className.indexOf('new') > -1) {
23491                         if (month == 11) {
23492                             month = 0;
23493                             year += 1;
23494                         } else {
23495                             month += 1;
23496                         }
23497                     }
23498                     //Roo.log([year,month,day]);
23499                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23500                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23501 //                    this.fill();
23502                     //Roo.log(this.formatDate(this.date));
23503                     this.setValue(this.formatDate(this.date));
23504                     this.hidePopup();
23505                 }
23506                 break;
23507         }
23508     },
23509     
23510     setStartDate: function(startDate)
23511     {
23512         this.startDate = startDate || -Infinity;
23513         if (this.startDate !== -Infinity) {
23514             this.startDate = this.parseDate(this.startDate);
23515         }
23516         this.update();
23517         this.updateNavArrows();
23518     },
23519
23520     setEndDate: function(endDate)
23521     {
23522         this.endDate = endDate || Infinity;
23523         if (this.endDate !== Infinity) {
23524             this.endDate = this.parseDate(this.endDate);
23525         }
23526         this.update();
23527         this.updateNavArrows();
23528     },
23529     
23530     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23531     {
23532         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23533         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23534             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23535         }
23536         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23537             return parseInt(d, 10);
23538         });
23539         this.update();
23540         this.updateNavArrows();
23541     },
23542     
23543     updateNavArrows: function() 
23544     {
23545         if(this.singleMode){
23546             return;
23547         }
23548         
23549         var d = new Date(this.viewDate),
23550         year = d.getUTCFullYear(),
23551         month = d.getUTCMonth();
23552         
23553         Roo.each(this.picker().select('.prev', true).elements, function(v){
23554             v.show();
23555             switch (this.viewMode) {
23556                 case 0:
23557
23558                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23559                         v.hide();
23560                     }
23561                     break;
23562                 case 1:
23563                 case 2:
23564                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23565                         v.hide();
23566                     }
23567                     break;
23568             }
23569         });
23570         
23571         Roo.each(this.picker().select('.next', true).elements, function(v){
23572             v.show();
23573             switch (this.viewMode) {
23574                 case 0:
23575
23576                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23577                         v.hide();
23578                     }
23579                     break;
23580                 case 1:
23581                 case 2:
23582                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23583                         v.hide();
23584                     }
23585                     break;
23586             }
23587         })
23588     },
23589     
23590     moveMonth: function(date, dir)
23591     {
23592         if (!dir) {
23593             return date;
23594         }
23595         var new_date = new Date(date.valueOf()),
23596         day = new_date.getUTCDate(),
23597         month = new_date.getUTCMonth(),
23598         mag = Math.abs(dir),
23599         new_month, test;
23600         dir = dir > 0 ? 1 : -1;
23601         if (mag == 1){
23602             test = dir == -1
23603             // If going back one month, make sure month is not current month
23604             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23605             ? function(){
23606                 return new_date.getUTCMonth() == month;
23607             }
23608             // If going forward one month, make sure month is as expected
23609             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23610             : function(){
23611                 return new_date.getUTCMonth() != new_month;
23612             };
23613             new_month = month + dir;
23614             new_date.setUTCMonth(new_month);
23615             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23616             if (new_month < 0 || new_month > 11) {
23617                 new_month = (new_month + 12) % 12;
23618             }
23619         } else {
23620             // For magnitudes >1, move one month at a time...
23621             for (var i=0; i<mag; i++) {
23622                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23623                 new_date = this.moveMonth(new_date, dir);
23624             }
23625             // ...then reset the day, keeping it in the new month
23626             new_month = new_date.getUTCMonth();
23627             new_date.setUTCDate(day);
23628             test = function(){
23629                 return new_month != new_date.getUTCMonth();
23630             };
23631         }
23632         // Common date-resetting loop -- if date is beyond end of month, make it
23633         // end of month
23634         while (test()){
23635             new_date.setUTCDate(--day);
23636             new_date.setUTCMonth(new_month);
23637         }
23638         return new_date;
23639     },
23640
23641     moveYear: function(date, dir)
23642     {
23643         return this.moveMonth(date, dir*12);
23644     },
23645
23646     dateWithinRange: function(date)
23647     {
23648         return date >= this.startDate && date <= this.endDate;
23649     },
23650
23651     
23652     remove: function() 
23653     {
23654         this.picker().remove();
23655     },
23656     
23657     validateValue : function(value)
23658     {
23659         if(this.getVisibilityEl().hasClass('hidden')){
23660             return true;
23661         }
23662         
23663         if(value.length < 1)  {
23664             if(this.allowBlank){
23665                 return true;
23666             }
23667             return false;
23668         }
23669         
23670         if(value.length < this.minLength){
23671             return false;
23672         }
23673         if(value.length > this.maxLength){
23674             return false;
23675         }
23676         if(this.vtype){
23677             var vt = Roo.form.VTypes;
23678             if(!vt[this.vtype](value, this)){
23679                 return false;
23680             }
23681         }
23682         if(typeof this.validator == "function"){
23683             var msg = this.validator(value);
23684             if(msg !== true){
23685                 return false;
23686             }
23687         }
23688         
23689         if(this.regex && !this.regex.test(value)){
23690             return false;
23691         }
23692         
23693         if(typeof(this.parseDate(value)) == 'undefined'){
23694             return false;
23695         }
23696         
23697         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23698             return false;
23699         }      
23700         
23701         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23702             return false;
23703         } 
23704         
23705         
23706         return true;
23707     },
23708     
23709     reset : function()
23710     {
23711         this.date = this.viewDate = '';
23712         
23713         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23714     }
23715    
23716 });
23717
23718 Roo.apply(Roo.bootstrap.form.DateField,  {
23719     
23720     head : {
23721         tag: 'thead',
23722         cn: [
23723         {
23724             tag: 'tr',
23725             cn: [
23726             {
23727                 tag: 'th',
23728                 cls: 'prev',
23729                 html: '<i class="fa fa-arrow-left"/>'
23730             },
23731             {
23732                 tag: 'th',
23733                 cls: 'switch',
23734                 colspan: '5'
23735             },
23736             {
23737                 tag: 'th',
23738                 cls: 'next',
23739                 html: '<i class="fa fa-arrow-right"/>'
23740             }
23741
23742             ]
23743         }
23744         ]
23745     },
23746     
23747     content : {
23748         tag: 'tbody',
23749         cn: [
23750         {
23751             tag: 'tr',
23752             cn: [
23753             {
23754                 tag: 'td',
23755                 colspan: '7'
23756             }
23757             ]
23758         }
23759         ]
23760     },
23761     
23762     footer : {
23763         tag: 'tfoot',
23764         cn: [
23765         {
23766             tag: 'tr',
23767             cn: [
23768             {
23769                 tag: 'th',
23770                 colspan: '7',
23771                 cls: 'today'
23772             }
23773                     
23774             ]
23775         }
23776         ]
23777     },
23778     
23779     dates:{
23780         en: {
23781             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23782             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23783             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23784             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23785             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23786             today: "Today"
23787         }
23788     },
23789     
23790     modes: [
23791     {
23792         clsName: 'days',
23793         navFnc: 'Month',
23794         navStep: 1
23795     },
23796     {
23797         clsName: 'months',
23798         navFnc: 'FullYear',
23799         navStep: 1
23800     },
23801     {
23802         clsName: 'years',
23803         navFnc: 'FullYear',
23804         navStep: 10
23805     }]
23806 });
23807
23808 Roo.apply(Roo.bootstrap.form.DateField,  {
23809   
23810     template : {
23811         tag: 'div',
23812         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23813         cn: [
23814         {
23815             tag: 'div',
23816             cls: 'datepicker-days',
23817             cn: [
23818             {
23819                 tag: 'table',
23820                 cls: 'table-condensed',
23821                 cn:[
23822                 Roo.bootstrap.form.DateField.head,
23823                 {
23824                     tag: 'tbody'
23825                 },
23826                 Roo.bootstrap.form.DateField.footer
23827                 ]
23828             }
23829             ]
23830         },
23831         {
23832             tag: 'div',
23833             cls: 'datepicker-months',
23834             cn: [
23835             {
23836                 tag: 'table',
23837                 cls: 'table-condensed',
23838                 cn:[
23839                 Roo.bootstrap.form.DateField.head,
23840                 Roo.bootstrap.form.DateField.content,
23841                 Roo.bootstrap.form.DateField.footer
23842                 ]
23843             }
23844             ]
23845         },
23846         {
23847             tag: 'div',
23848             cls: 'datepicker-years',
23849             cn: [
23850             {
23851                 tag: 'table',
23852                 cls: 'table-condensed',
23853                 cn:[
23854                 Roo.bootstrap.form.DateField.head,
23855                 Roo.bootstrap.form.DateField.content,
23856                 Roo.bootstrap.form.DateField.footer
23857                 ]
23858             }
23859             ]
23860         }
23861         ]
23862     }
23863 });
23864
23865  
23866
23867  /*
23868  * - LGPL
23869  *
23870  * TimeField
23871  * 
23872  */
23873
23874 /**
23875  * @class Roo.bootstrap.form.TimeField
23876  * @extends Roo.bootstrap.form.Input
23877  * Bootstrap DateField class
23878  * 
23879  * 
23880  * @constructor
23881  * Create a new TimeField
23882  * @param {Object} config The config object
23883  */
23884
23885 Roo.bootstrap.form.TimeField = function(config){
23886     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23887     this.addEvents({
23888             /**
23889              * @event show
23890              * Fires when this field show.
23891              * @param {Roo.bootstrap.form.DateField} thisthis
23892              * @param {Mixed} date The date value
23893              */
23894             show : true,
23895             /**
23896              * @event show
23897              * Fires when this field hide.
23898              * @param {Roo.bootstrap.form.DateField} this
23899              * @param {Mixed} date The date value
23900              */
23901             hide : true,
23902             /**
23903              * @event select
23904              * Fires when select a date.
23905              * @param {Roo.bootstrap.form.DateField} this
23906              * @param {Mixed} date The date value
23907              */
23908             select : true
23909         });
23910 };
23911
23912 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23913     
23914     /**
23915      * @cfg {String} format
23916      * The default time format string which can be overriden for localization support.  The format must be
23917      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23918      */
23919     format : "H:i",
23920
23921     getAutoCreate : function()
23922     {
23923         this.after = '<i class="fa far fa-clock"></i>';
23924         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23925         
23926          
23927     },
23928     onRender: function(ct, position)
23929     {
23930         
23931         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23932                 
23933         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23934         
23935         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23936         
23937         this.pop = this.picker().select('>.datepicker-time',true).first();
23938         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23939         
23940         this.picker().on('mousedown', this.onMousedown, this);
23941         this.picker().on('click', this.onClick, this);
23942         
23943         this.picker().addClass('datepicker-dropdown');
23944     
23945         this.fillTime();
23946         this.update();
23947             
23948         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23949         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23950         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23951         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23952         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23953         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23954
23955     },
23956     
23957     fireKey: function(e){
23958         if (!this.picker().isVisible()){
23959             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23960                 this.show();
23961             }
23962             return;
23963         }
23964
23965         e.preventDefault();
23966         
23967         switch(e.keyCode){
23968             case 27: // escape
23969                 this.hide();
23970                 break;
23971             case 37: // left
23972             case 39: // right
23973                 this.onTogglePeriod();
23974                 break;
23975             case 38: // up
23976                 this.onIncrementMinutes();
23977                 break;
23978             case 40: // down
23979                 this.onDecrementMinutes();
23980                 break;
23981             case 13: // enter
23982             case 9: // tab
23983                 this.setTime();
23984                 break;
23985         }
23986     },
23987     
23988     onClick: function(e) {
23989         e.stopPropagation();
23990         e.preventDefault();
23991     },
23992     
23993     picker : function()
23994     {
23995         return this.pickerEl;
23996     },
23997     
23998     fillTime: function()
23999     {    
24000         var time = this.pop.select('tbody', true).first();
24001         
24002         time.dom.innerHTML = '';
24003         
24004         time.createChild({
24005             tag: 'tr',
24006             cn: [
24007                 {
24008                     tag: 'td',
24009                     cn: [
24010                         {
24011                             tag: 'a',
24012                             href: '#',
24013                             cls: 'btn',
24014                             cn: [
24015                                 {
24016                                     tag: 'i',
24017                                     cls: 'hours-up fa fas fa-chevron-up'
24018                                 }
24019                             ]
24020                         } 
24021                     ]
24022                 },
24023                 {
24024                     tag: 'td',
24025                     cls: 'separator'
24026                 },
24027                 {
24028                     tag: 'td',
24029                     cn: [
24030                         {
24031                             tag: 'a',
24032                             href: '#',
24033                             cls: 'btn',
24034                             cn: [
24035                                 {
24036                                     tag: 'i',
24037                                     cls: 'minutes-up fa fas fa-chevron-up'
24038                                 }
24039                             ]
24040                         }
24041                     ]
24042                 },
24043                 {
24044                     tag: 'td',
24045                     cls: 'separator'
24046                 }
24047             ]
24048         });
24049         
24050         time.createChild({
24051             tag: 'tr',
24052             cn: [
24053                 {
24054                     tag: 'td',
24055                     cn: [
24056                         {
24057                             tag: 'span',
24058                             cls: 'timepicker-hour',
24059                             html: '00'
24060                         }  
24061                     ]
24062                 },
24063                 {
24064                     tag: 'td',
24065                     cls: 'separator',
24066                     html: ':'
24067                 },
24068                 {
24069                     tag: 'td',
24070                     cn: [
24071                         {
24072                             tag: 'span',
24073                             cls: 'timepicker-minute',
24074                             html: '00'
24075                         }  
24076                     ]
24077                 },
24078                 {
24079                     tag: 'td',
24080                     cls: 'separator'
24081                 },
24082                 {
24083                     tag: 'td',
24084                     cn: [
24085                         {
24086                             tag: 'button',
24087                             type: 'button',
24088                             cls: 'btn btn-primary period',
24089                             html: 'AM'
24090                             
24091                         }
24092                     ]
24093                 }
24094             ]
24095         });
24096         
24097         time.createChild({
24098             tag: 'tr',
24099             cn: [
24100                 {
24101                     tag: 'td',
24102                     cn: [
24103                         {
24104                             tag: 'a',
24105                             href: '#',
24106                             cls: 'btn',
24107                             cn: [
24108                                 {
24109                                     tag: 'span',
24110                                     cls: 'hours-down fa fas fa-chevron-down'
24111                                 }
24112                             ]
24113                         }
24114                     ]
24115                 },
24116                 {
24117                     tag: 'td',
24118                     cls: 'separator'
24119                 },
24120                 {
24121                     tag: 'td',
24122                     cn: [
24123                         {
24124                             tag: 'a',
24125                             href: '#',
24126                             cls: 'btn',
24127                             cn: [
24128                                 {
24129                                     tag: 'span',
24130                                     cls: 'minutes-down fa fas fa-chevron-down'
24131                                 }
24132                             ]
24133                         }
24134                     ]
24135                 },
24136                 {
24137                     tag: 'td',
24138                     cls: 'separator'
24139                 }
24140             ]
24141         });
24142         
24143     },
24144     
24145     update: function()
24146     {
24147         
24148         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24149         
24150         this.fill();
24151     },
24152     
24153     fill: function() 
24154     {
24155         var hours = this.time.getHours();
24156         var minutes = this.time.getMinutes();
24157         var period = 'AM';
24158         
24159         if(hours > 11){
24160             period = 'PM';
24161         }
24162         
24163         if(hours == 0){
24164             hours = 12;
24165         }
24166         
24167         
24168         if(hours > 12){
24169             hours = hours - 12;
24170         }
24171         
24172         if(hours < 10){
24173             hours = '0' + hours;
24174         }
24175         
24176         if(minutes < 10){
24177             minutes = '0' + minutes;
24178         }
24179         
24180         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24181         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24182         this.pop.select('button', true).first().dom.innerHTML = period;
24183         
24184     },
24185     
24186     place: function()
24187     {   
24188         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24189         
24190         var cls = ['bottom'];
24191         
24192         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24193             cls.pop();
24194             cls.push('top');
24195         }
24196         
24197         cls.push('right');
24198         
24199         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24200             cls.pop();
24201             cls.push('left');
24202         }
24203         //this.picker().setXY(20000,20000);
24204         this.picker().addClass(cls.join('-'));
24205         
24206         var _this = this;
24207         
24208         Roo.each(cls, function(c){
24209             if(c == 'bottom'){
24210                 (function() {
24211                  //  
24212                 }).defer(200);
24213                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24214                 //_this.picker().setTop(_this.inputEl().getHeight());
24215                 return;
24216             }
24217             if(c == 'top'){
24218                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24219                 
24220                 //_this.picker().setTop(0 - _this.picker().getHeight());
24221                 return;
24222             }
24223             /*
24224             if(c == 'left'){
24225                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24226                 return;
24227             }
24228             if(c == 'right'){
24229                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24230                 return;
24231             }
24232             */
24233         });
24234         
24235     },
24236   
24237     onFocus : function()
24238     {
24239         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24240         this.show();
24241     },
24242     
24243     onBlur : function()
24244     {
24245         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24246         this.hide();
24247     },
24248     
24249     show : function()
24250     {
24251         this.picker().show();
24252         this.pop.show();
24253         this.update();
24254         this.place();
24255         
24256         this.fireEvent('show', this, this.date);
24257     },
24258     
24259     hide : function()
24260     {
24261         this.picker().hide();
24262         this.pop.hide();
24263         
24264         this.fireEvent('hide', this, this.date);
24265     },
24266     
24267     setTime : function()
24268     {
24269         this.hide();
24270         this.setValue(this.time.format(this.format));
24271         
24272         this.fireEvent('select', this, this.date);
24273         
24274         
24275     },
24276     
24277     onMousedown: function(e){
24278         e.stopPropagation();
24279         e.preventDefault();
24280     },
24281     
24282     onIncrementHours: function()
24283     {
24284         Roo.log('onIncrementHours');
24285         this.time = this.time.add(Date.HOUR, 1);
24286         this.update();
24287         
24288     },
24289     
24290     onDecrementHours: function()
24291     {
24292         Roo.log('onDecrementHours');
24293         this.time = this.time.add(Date.HOUR, -1);
24294         this.update();
24295     },
24296     
24297     onIncrementMinutes: function()
24298     {
24299         Roo.log('onIncrementMinutes');
24300         this.time = this.time.add(Date.MINUTE, 1);
24301         this.update();
24302     },
24303     
24304     onDecrementMinutes: function()
24305     {
24306         Roo.log('onDecrementMinutes');
24307         this.time = this.time.add(Date.MINUTE, -1);
24308         this.update();
24309     },
24310     
24311     onTogglePeriod: function()
24312     {
24313         Roo.log('onTogglePeriod');
24314         this.time = this.time.add(Date.HOUR, 12);
24315         this.update();
24316     }
24317     
24318    
24319 });
24320  
24321
24322 Roo.apply(Roo.bootstrap.form.TimeField,  {
24323   
24324     template : {
24325         tag: 'div',
24326         cls: 'datepicker dropdown-menu',
24327         cn: [
24328             {
24329                 tag: 'div',
24330                 cls: 'datepicker-time',
24331                 cn: [
24332                 {
24333                     tag: 'table',
24334                     cls: 'table-condensed',
24335                     cn:[
24336                         {
24337                             tag: 'tbody',
24338                             cn: [
24339                                 {
24340                                     tag: 'tr',
24341                                     cn: [
24342                                     {
24343                                         tag: 'td',
24344                                         colspan: '7'
24345                                     }
24346                                     ]
24347                                 }
24348                             ]
24349                         },
24350                         {
24351                             tag: 'tfoot',
24352                             cn: [
24353                                 {
24354                                     tag: 'tr',
24355                                     cn: [
24356                                     {
24357                                         tag: 'th',
24358                                         colspan: '7',
24359                                         cls: '',
24360                                         cn: [
24361                                             {
24362                                                 tag: 'button',
24363                                                 cls: 'btn btn-info ok',
24364                                                 html: 'OK'
24365                                             }
24366                                         ]
24367                                     }
24368                     
24369                                     ]
24370                                 }
24371                             ]
24372                         }
24373                     ]
24374                 }
24375                 ]
24376             }
24377         ]
24378     }
24379 });
24380
24381  
24382
24383  /*
24384  * - LGPL
24385  *
24386  * MonthField
24387  * 
24388  */
24389
24390 /**
24391  * @class Roo.bootstrap.form.MonthField
24392  * @extends Roo.bootstrap.form.Input
24393  * Bootstrap MonthField class
24394  * 
24395  * @cfg {String} language default en
24396  * 
24397  * @constructor
24398  * Create a new MonthField
24399  * @param {Object} config The config object
24400  */
24401
24402 Roo.bootstrap.form.MonthField = function(config){
24403     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24404     
24405     this.addEvents({
24406         /**
24407          * @event show
24408          * Fires when this field show.
24409          * @param {Roo.bootstrap.form.MonthField} this
24410          * @param {Mixed} date The date value
24411          */
24412         show : true,
24413         /**
24414          * @event show
24415          * Fires when this field hide.
24416          * @param {Roo.bootstrap.form.MonthField} this
24417          * @param {Mixed} date The date value
24418          */
24419         hide : true,
24420         /**
24421          * @event select
24422          * Fires when select a date.
24423          * @param {Roo.bootstrap.form.MonthField} this
24424          * @param {String} oldvalue The old value
24425          * @param {String} newvalue The new value
24426          */
24427         select : true
24428     });
24429 };
24430
24431 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24432     
24433     onRender: function(ct, position)
24434     {
24435         
24436         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24437         
24438         this.language = this.language || 'en';
24439         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24440         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24441         
24442         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24443         this.isInline = false;
24444         this.isInput = true;
24445         this.component = this.el.select('.add-on', true).first() || false;
24446         this.component = (this.component && this.component.length === 0) ? false : this.component;
24447         this.hasInput = this.component && this.inputEL().length;
24448         
24449         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24450         
24451         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24452         
24453         this.picker().on('mousedown', this.onMousedown, this);
24454         this.picker().on('click', this.onClick, this);
24455         
24456         this.picker().addClass('datepicker-dropdown');
24457         
24458         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24459             v.setStyle('width', '189px');
24460         });
24461         
24462         this.fillMonths();
24463         
24464         this.update();
24465         
24466         if(this.isInline) {
24467             this.show();
24468         }
24469         
24470     },
24471     
24472     setValue: function(v, suppressEvent)
24473     {   
24474         var o = this.getValue();
24475         
24476         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24477         
24478         this.update();
24479
24480         if(suppressEvent !== true){
24481             this.fireEvent('select', this, o, v);
24482         }
24483         
24484     },
24485     
24486     getValue: function()
24487     {
24488         return this.value;
24489     },
24490     
24491     onClick: function(e) 
24492     {
24493         e.stopPropagation();
24494         e.preventDefault();
24495         
24496         var target = e.getTarget();
24497         
24498         if(target.nodeName.toLowerCase() === 'i'){
24499             target = Roo.get(target).dom.parentNode;
24500         }
24501         
24502         var nodeName = target.nodeName;
24503         var className = target.className;
24504         var html = target.innerHTML;
24505         
24506         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24507             return;
24508         }
24509         
24510         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24511         
24512         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24513         
24514         this.hide();
24515                         
24516     },
24517     
24518     picker : function()
24519     {
24520         return this.pickerEl;
24521     },
24522     
24523     fillMonths: function()
24524     {    
24525         var i = 0;
24526         var months = this.picker().select('>.datepicker-months td', true).first();
24527         
24528         months.dom.innerHTML = '';
24529         
24530         while (i < 12) {
24531             var month = {
24532                 tag: 'span',
24533                 cls: 'month',
24534                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24535             };
24536             
24537             months.createChild(month);
24538         }
24539         
24540     },
24541     
24542     update: function()
24543     {
24544         var _this = this;
24545         
24546         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24547             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24548         }
24549         
24550         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24551             e.removeClass('active');
24552             
24553             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24554                 e.addClass('active');
24555             }
24556         })
24557     },
24558     
24559     place: function()
24560     {
24561         if(this.isInline) {
24562             return;
24563         }
24564         
24565         this.picker().removeClass(['bottom', 'top']);
24566         
24567         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24568             /*
24569              * place to the top of element!
24570              *
24571              */
24572             
24573             this.picker().addClass('top');
24574             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24575             
24576             return;
24577         }
24578         
24579         this.picker().addClass('bottom');
24580         
24581         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24582     },
24583     
24584     onFocus : function()
24585     {
24586         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24587         this.show();
24588     },
24589     
24590     onBlur : function()
24591     {
24592         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24593         
24594         var d = this.inputEl().getValue();
24595         
24596         this.setValue(d);
24597                 
24598         this.hide();
24599     },
24600     
24601     show : function()
24602     {
24603         this.picker().show();
24604         this.picker().select('>.datepicker-months', true).first().show();
24605         this.update();
24606         this.place();
24607         
24608         this.fireEvent('show', this, this.date);
24609     },
24610     
24611     hide : function()
24612     {
24613         if(this.isInline) {
24614             return;
24615         }
24616         this.picker().hide();
24617         this.fireEvent('hide', this, this.date);
24618         
24619     },
24620     
24621     onMousedown: function(e)
24622     {
24623         e.stopPropagation();
24624         e.preventDefault();
24625     },
24626     
24627     keyup: function(e)
24628     {
24629         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24630         this.update();
24631     },
24632
24633     fireKey: function(e)
24634     {
24635         if (!this.picker().isVisible()){
24636             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24637                 this.show();
24638             }
24639             return;
24640         }
24641         
24642         var dir;
24643         
24644         switch(e.keyCode){
24645             case 27: // escape
24646                 this.hide();
24647                 e.preventDefault();
24648                 break;
24649             case 37: // left
24650             case 39: // right
24651                 dir = e.keyCode == 37 ? -1 : 1;
24652                 
24653                 this.vIndex = this.vIndex + dir;
24654                 
24655                 if(this.vIndex < 0){
24656                     this.vIndex = 0;
24657                 }
24658                 
24659                 if(this.vIndex > 11){
24660                     this.vIndex = 11;
24661                 }
24662                 
24663                 if(isNaN(this.vIndex)){
24664                     this.vIndex = 0;
24665                 }
24666                 
24667                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24668                 
24669                 break;
24670             case 38: // up
24671             case 40: // down
24672                 
24673                 dir = e.keyCode == 38 ? -1 : 1;
24674                 
24675                 this.vIndex = this.vIndex + dir * 4;
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                 break;
24691                 
24692             case 13: // enter
24693                 
24694                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24695                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24696                 }
24697                 
24698                 this.hide();
24699                 e.preventDefault();
24700                 break;
24701             case 9: // tab
24702                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24703                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24704                 }
24705                 this.hide();
24706                 break;
24707             case 16: // shift
24708             case 17: // ctrl
24709             case 18: // alt
24710                 break;
24711             default :
24712                 this.hide();
24713                 
24714         }
24715     },
24716     
24717     remove: function() 
24718     {
24719         this.picker().remove();
24720     }
24721    
24722 });
24723
24724 Roo.apply(Roo.bootstrap.form.MonthField,  {
24725     
24726     content : {
24727         tag: 'tbody',
24728         cn: [
24729         {
24730             tag: 'tr',
24731             cn: [
24732             {
24733                 tag: 'td',
24734                 colspan: '7'
24735             }
24736             ]
24737         }
24738         ]
24739     },
24740     
24741     dates:{
24742         en: {
24743             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24744             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24745         }
24746     }
24747 });
24748
24749 Roo.apply(Roo.bootstrap.form.MonthField,  {
24750   
24751     template : {
24752         tag: 'div',
24753         cls: 'datepicker dropdown-menu roo-dynamic',
24754         cn: [
24755             {
24756                 tag: 'div',
24757                 cls: 'datepicker-months',
24758                 cn: [
24759                 {
24760                     tag: 'table',
24761                     cls: 'table-condensed',
24762                     cn:[
24763                         Roo.bootstrap.form.DateField.content
24764                     ]
24765                 }
24766                 ]
24767             }
24768         ]
24769     }
24770 });
24771
24772  
24773
24774  
24775  /*
24776  * - LGPL
24777  *
24778  * CheckBox
24779  * 
24780  */
24781
24782 /**
24783  * @class Roo.bootstrap.form.CheckBox
24784  * @extends Roo.bootstrap.form.Input
24785  * Bootstrap CheckBox class
24786  * 
24787  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24788  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24789  * @cfg {String} boxLabel The text that appears beside the checkbox
24790  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24791  * @cfg {Boolean} checked initnal the element
24792  * @cfg {Boolean} inline inline the element (default false)
24793  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24794  * @cfg {String} tooltip label tooltip
24795  * 
24796  * @constructor
24797  * Create a new CheckBox
24798  * @param {Object} config The config object
24799  */
24800
24801 Roo.bootstrap.form.CheckBox = function(config){
24802     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24803    
24804     this.addEvents({
24805         /**
24806         * @event check
24807         * Fires when the element is checked or unchecked.
24808         * @param {Roo.bootstrap.form.CheckBox} this This input
24809         * @param {Boolean} checked The new checked value
24810         */
24811        check : true,
24812        /**
24813         * @event click
24814         * Fires when the element is click.
24815         * @param {Roo.bootstrap.form.CheckBox} this This input
24816         */
24817        click : true
24818     });
24819     
24820 };
24821
24822 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24823   
24824     inputType: 'checkbox',
24825     inputValue: 1,
24826     valueOff: 0,
24827     boxLabel: false,
24828     checked: false,
24829     weight : false,
24830     inline: false,
24831     tooltip : '',
24832     
24833     // checkbox success does not make any sense really.. 
24834     invalidClass : "",
24835     validClass : "",
24836     
24837     
24838     getAutoCreate : function()
24839     {
24840         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24841         
24842         var id = Roo.id();
24843         
24844         var cfg = {};
24845         
24846         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24847         
24848         if(this.inline){
24849             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24850         }
24851         
24852         var input =  {
24853             tag: 'input',
24854             id : id,
24855             type : this.inputType,
24856             value : this.inputValue,
24857             cls : 'roo-' + this.inputType, //'form-box',
24858             placeholder : this.placeholder || ''
24859             
24860         };
24861         
24862         if(this.inputType != 'radio'){
24863             var hidden =  {
24864                 tag: 'input',
24865                 type : 'hidden',
24866                 cls : 'roo-hidden-value',
24867                 value : this.checked ? this.inputValue : this.valueOff
24868             };
24869         }
24870         
24871             
24872         if (this.weight) { // Validity check?
24873             cfg.cls += " " + this.inputType + "-" + this.weight;
24874         }
24875         
24876         if (this.disabled) {
24877             input.disabled=true;
24878         }
24879         
24880         if(this.checked){
24881             input.checked = this.checked;
24882         }
24883         
24884         if (this.name) {
24885             
24886             input.name = this.name;
24887             
24888             if(this.inputType != 'radio'){
24889                 hidden.name = this.name;
24890                 input.name = '_hidden_' + this.name;
24891             }
24892         }
24893         
24894         if (this.size) {
24895             input.cls += ' input-' + this.size;
24896         }
24897         
24898         var settings=this;
24899         
24900         ['xs','sm','md','lg'].map(function(size){
24901             if (settings[size]) {
24902                 cfg.cls += ' col-' + size + '-' + settings[size];
24903             }
24904         });
24905         
24906         var inputblock = input;
24907          
24908         if (this.before || this.after) {
24909             
24910             inputblock = {
24911                 cls : 'input-group',
24912                 cn :  [] 
24913             };
24914             
24915             if (this.before) {
24916                 inputblock.cn.push({
24917                     tag :'span',
24918                     cls : 'input-group-addon',
24919                     html : this.before
24920                 });
24921             }
24922             
24923             inputblock.cn.push(input);
24924             
24925             if(this.inputType != 'radio'){
24926                 inputblock.cn.push(hidden);
24927             }
24928             
24929             if (this.after) {
24930                 inputblock.cn.push({
24931                     tag :'span',
24932                     cls : 'input-group-addon',
24933                     html : this.after
24934                 });
24935             }
24936             
24937         }
24938         var boxLabelCfg = false;
24939         
24940         if(this.boxLabel){
24941            
24942             boxLabelCfg = {
24943                 tag: 'label',
24944                 //'for': id, // box label is handled by onclick - so no for...
24945                 cls: 'box-label',
24946                 html: this.boxLabel
24947             };
24948             if(this.tooltip){
24949                 boxLabelCfg.tooltip = this.tooltip;
24950             }
24951              
24952         }
24953         
24954         
24955         if (align ==='left' && this.fieldLabel.length) {
24956 //                Roo.log("left and has label");
24957             cfg.cn = [
24958                 {
24959                     tag: 'label',
24960                     'for' :  id,
24961                     cls : 'control-label',
24962                     html : this.fieldLabel
24963                 },
24964                 {
24965                     cls : "", 
24966                     cn: [
24967                         inputblock
24968                     ]
24969                 }
24970             ];
24971             
24972             if (boxLabelCfg) {
24973                 cfg.cn[1].cn.push(boxLabelCfg);
24974             }
24975             
24976             if(this.labelWidth > 12){
24977                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24978             }
24979             
24980             if(this.labelWidth < 13 && this.labelmd == 0){
24981                 this.labelmd = this.labelWidth;
24982             }
24983             
24984             if(this.labellg > 0){
24985                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24986                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24987             }
24988             
24989             if(this.labelmd > 0){
24990                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24991                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24992             }
24993             
24994             if(this.labelsm > 0){
24995                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24996                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24997             }
24998             
24999             if(this.labelxs > 0){
25000                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25001                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25002             }
25003             
25004         } else if ( this.fieldLabel.length) {
25005 //                Roo.log(" label");
25006                 cfg.cn = [
25007                    
25008                     {
25009                         tag: this.boxLabel ? 'span' : 'label',
25010                         'for': id,
25011                         cls: 'control-label box-input-label',
25012                         //cls : 'input-group-addon',
25013                         html : this.fieldLabel
25014                     },
25015                     
25016                     inputblock
25017                     
25018                 ];
25019                 if (boxLabelCfg) {
25020                     cfg.cn.push(boxLabelCfg);
25021                 }
25022
25023         } else {
25024             
25025 //                Roo.log(" no label && no align");
25026                 cfg.cn = [  inputblock ] ;
25027                 if (boxLabelCfg) {
25028                     cfg.cn.push(boxLabelCfg);
25029                 }
25030
25031                 
25032         }
25033         
25034        
25035         
25036         if(this.inputType != 'radio'){
25037             cfg.cn.push(hidden);
25038         }
25039         
25040         return cfg;
25041         
25042     },
25043     
25044     /**
25045      * return the real input element.
25046      */
25047     inputEl: function ()
25048     {
25049         return this.el.select('input.roo-' + this.inputType,true).first();
25050     },
25051     hiddenEl: function ()
25052     {
25053         return this.el.select('input.roo-hidden-value',true).first();
25054     },
25055     
25056     labelEl: function()
25057     {
25058         return this.el.select('label.control-label',true).first();
25059     },
25060     /* depricated... */
25061     
25062     label: function()
25063     {
25064         return this.labelEl();
25065     },
25066     
25067     boxLabelEl: function()
25068     {
25069         return this.el.select('label.box-label',true).first();
25070     },
25071     
25072     initEvents : function()
25073     {
25074 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25075         
25076         this.inputEl().on('click', this.onClick,  this);
25077         
25078         if (this.boxLabel) { 
25079             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25080         }
25081         
25082         this.startValue = this.getValue();
25083         
25084         if(this.groupId){
25085             Roo.bootstrap.form.CheckBox.register(this);
25086         }
25087     },
25088     
25089     onClick : function(e)
25090     {   
25091         if(this.fireEvent('click', this, e) !== false){
25092             this.setChecked(!this.checked);
25093         }
25094         
25095     },
25096     
25097     setChecked : function(state,suppressEvent)
25098     {
25099         this.startValue = this.getValue();
25100
25101         if(this.inputType == 'radio'){
25102             
25103             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25104                 e.dom.checked = false;
25105             });
25106             
25107             this.inputEl().dom.checked = true;
25108             
25109             this.inputEl().dom.value = this.inputValue;
25110             
25111             if(suppressEvent !== true){
25112                 this.fireEvent('check', this, true);
25113             }
25114             
25115             this.validate();
25116             
25117             return;
25118         }
25119         
25120         this.checked = state;
25121         
25122         this.inputEl().dom.checked = state;
25123         
25124         
25125         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25126         
25127         if(suppressEvent !== true){
25128             this.fireEvent('check', this, state);
25129         }
25130         
25131         this.validate();
25132     },
25133     
25134     getValue : function()
25135     {
25136         if(this.inputType == 'radio'){
25137             return this.getGroupValue();
25138         }
25139         
25140         return this.hiddenEl().dom.value;
25141         
25142     },
25143     
25144     getGroupValue : function()
25145     {
25146         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25147             return '';
25148         }
25149         
25150         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25151     },
25152     
25153     setValue : function(v,suppressEvent)
25154     {
25155         if(this.inputType == 'radio'){
25156             this.setGroupValue(v, suppressEvent);
25157             return;
25158         }
25159         
25160         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25161         
25162         this.validate();
25163     },
25164     
25165     setGroupValue : function(v, suppressEvent)
25166     {
25167         this.startValue = this.getValue();
25168         
25169         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25170             e.dom.checked = false;
25171             
25172             if(e.dom.value == v){
25173                 e.dom.checked = true;
25174             }
25175         });
25176         
25177         if(suppressEvent !== true){
25178             this.fireEvent('check', this, true);
25179         }
25180
25181         this.validate();
25182         
25183         return;
25184     },
25185     
25186     validate : function()
25187     {
25188         if(this.getVisibilityEl().hasClass('hidden')){
25189             return true;
25190         }
25191         
25192         if(
25193                 this.disabled || 
25194                 (this.inputType == 'radio' && this.validateRadio()) ||
25195                 (this.inputType == 'checkbox' && this.validateCheckbox())
25196         ){
25197             this.markValid();
25198             return true;
25199         }
25200         
25201         this.markInvalid();
25202         return false;
25203     },
25204     
25205     validateRadio : function()
25206     {
25207         if(this.getVisibilityEl().hasClass('hidden')){
25208             return true;
25209         }
25210         
25211         if(this.allowBlank){
25212             return true;
25213         }
25214         
25215         var valid = false;
25216         
25217         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25218             if(!e.dom.checked){
25219                 return;
25220             }
25221             
25222             valid = true;
25223             
25224             return false;
25225         });
25226         
25227         return valid;
25228     },
25229     
25230     validateCheckbox : function()
25231     {
25232         if(!this.groupId){
25233             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25234             //return (this.getValue() == this.inputValue) ? true : false;
25235         }
25236         
25237         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25238         
25239         if(!group){
25240             return false;
25241         }
25242         
25243         var r = false;
25244         
25245         for(var i in group){
25246             if(group[i].el.isVisible(true)){
25247                 r = false;
25248                 break;
25249             }
25250             
25251             r = true;
25252         }
25253         
25254         for(var i in group){
25255             if(r){
25256                 break;
25257             }
25258             
25259             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25260         }
25261         
25262         return r;
25263     },
25264     
25265     /**
25266      * Mark this field as valid
25267      */
25268     markValid : function()
25269     {
25270         var _this = this;
25271         
25272         this.fireEvent('valid', this);
25273         
25274         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25275         
25276         if(this.groupId){
25277             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25278         }
25279         
25280         if(label){
25281             label.markValid();
25282         }
25283
25284         if(this.inputType == 'radio'){
25285             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25286                 var fg = e.findParent('.form-group', false, true);
25287                 if (Roo.bootstrap.version == 3) {
25288                     fg.removeClass([_this.invalidClass, _this.validClass]);
25289                     fg.addClass(_this.validClass);
25290                 } else {
25291                     fg.removeClass(['is-valid', 'is-invalid']);
25292                     fg.addClass('is-valid');
25293                 }
25294             });
25295             
25296             return;
25297         }
25298
25299         if(!this.groupId){
25300             var fg = this.el.findParent('.form-group', false, true);
25301             if (Roo.bootstrap.version == 3) {
25302                 fg.removeClass([this.invalidClass, this.validClass]);
25303                 fg.addClass(this.validClass);
25304             } else {
25305                 fg.removeClass(['is-valid', 'is-invalid']);
25306                 fg.addClass('is-valid');
25307             }
25308             return;
25309         }
25310         
25311         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25312         
25313         if(!group){
25314             return;
25315         }
25316         
25317         for(var i in group){
25318             var fg = group[i].el.findParent('.form-group', false, true);
25319             if (Roo.bootstrap.version == 3) {
25320                 fg.removeClass([this.invalidClass, this.validClass]);
25321                 fg.addClass(this.validClass);
25322             } else {
25323                 fg.removeClass(['is-valid', 'is-invalid']);
25324                 fg.addClass('is-valid');
25325             }
25326         }
25327     },
25328     
25329      /**
25330      * Mark this field as invalid
25331      * @param {String} msg The validation message
25332      */
25333     markInvalid : function(msg)
25334     {
25335         if(this.allowBlank){
25336             return;
25337         }
25338         
25339         var _this = this;
25340         
25341         this.fireEvent('invalid', this, msg);
25342         
25343         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25344         
25345         if(this.groupId){
25346             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25347         }
25348         
25349         if(label){
25350             label.markInvalid();
25351         }
25352             
25353         if(this.inputType == 'radio'){
25354             
25355             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25356                 var fg = e.findParent('.form-group', false, true);
25357                 if (Roo.bootstrap.version == 3) {
25358                     fg.removeClass([_this.invalidClass, _this.validClass]);
25359                     fg.addClass(_this.invalidClass);
25360                 } else {
25361                     fg.removeClass(['is-invalid', 'is-valid']);
25362                     fg.addClass('is-invalid');
25363                 }
25364             });
25365             
25366             return;
25367         }
25368         
25369         if(!this.groupId){
25370             var fg = this.el.findParent('.form-group', false, true);
25371             if (Roo.bootstrap.version == 3) {
25372                 fg.removeClass([_this.invalidClass, _this.validClass]);
25373                 fg.addClass(_this.invalidClass);
25374             } else {
25375                 fg.removeClass(['is-invalid', 'is-valid']);
25376                 fg.addClass('is-invalid');
25377             }
25378             return;
25379         }
25380         
25381         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25382         
25383         if(!group){
25384             return;
25385         }
25386         
25387         for(var i in group){
25388             var fg = group[i].el.findParent('.form-group', false, true);
25389             if (Roo.bootstrap.version == 3) {
25390                 fg.removeClass([_this.invalidClass, _this.validClass]);
25391                 fg.addClass(_this.invalidClass);
25392             } else {
25393                 fg.removeClass(['is-invalid', 'is-valid']);
25394                 fg.addClass('is-invalid');
25395             }
25396         }
25397         
25398     },
25399     
25400     clearInvalid : function()
25401     {
25402         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25403         
25404         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25405         
25406         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25407         
25408         if (label && label.iconEl) {
25409             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25410             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25411         }
25412     },
25413     
25414     disable : function()
25415     {
25416         if(this.inputType != 'radio'){
25417             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25418             return;
25419         }
25420         
25421         var _this = this;
25422         
25423         if(this.rendered){
25424             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25425                 _this.getActionEl().addClass(this.disabledClass);
25426                 e.dom.disabled = true;
25427             });
25428         }
25429         
25430         this.disabled = true;
25431         this.fireEvent("disable", this);
25432         return this;
25433     },
25434
25435     enable : function()
25436     {
25437         if(this.inputType != 'radio'){
25438             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25439             return;
25440         }
25441         
25442         var _this = this;
25443         
25444         if(this.rendered){
25445             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25446                 _this.getActionEl().removeClass(this.disabledClass);
25447                 e.dom.disabled = false;
25448             });
25449         }
25450         
25451         this.disabled = false;
25452         this.fireEvent("enable", this);
25453         return this;
25454     },
25455     
25456     setBoxLabel : function(v)
25457     {
25458         this.boxLabel = v;
25459         
25460         if(this.rendered){
25461             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25462         }
25463     }
25464
25465 });
25466
25467 Roo.apply(Roo.bootstrap.form.CheckBox, {
25468     
25469     groups: {},
25470     
25471      /**
25472     * register a CheckBox Group
25473     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25474     */
25475     register : function(checkbox)
25476     {
25477         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25478             this.groups[checkbox.groupId] = {};
25479         }
25480         
25481         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25482             return;
25483         }
25484         
25485         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25486         
25487     },
25488     /**
25489     * fetch a CheckBox Group based on the group ID
25490     * @param {string} the group ID
25491     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25492     */
25493     get: function(groupId) {
25494         if (typeof(this.groups[groupId]) == 'undefined') {
25495             return false;
25496         }
25497         
25498         return this.groups[groupId] ;
25499     }
25500     
25501     
25502 });
25503 /*
25504  * - LGPL
25505  *
25506  * RadioItem
25507  * 
25508  */
25509
25510 /**
25511  * @class Roo.bootstrap.form.Radio
25512  * @extends Roo.bootstrap.Component
25513  * Bootstrap Radio class
25514  * @cfg {String} boxLabel - the label associated
25515  * @cfg {String} value - the value of radio
25516  * 
25517  * @constructor
25518  * Create a new Radio
25519  * @param {Object} config The config object
25520  */
25521 Roo.bootstrap.form.Radio = function(config){
25522     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25523     
25524 };
25525
25526 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25527     
25528     boxLabel : '',
25529     
25530     value : '',
25531     
25532     getAutoCreate : function()
25533     {
25534         var cfg = {
25535             tag : 'div',
25536             cls : 'form-group radio',
25537             cn : [
25538                 {
25539                     tag : 'label',
25540                     cls : 'box-label',
25541                     html : this.boxLabel
25542                 }
25543             ]
25544         };
25545         
25546         return cfg;
25547     },
25548     
25549     initEvents : function() 
25550     {
25551         this.parent().register(this);
25552         
25553         this.el.on('click', this.onClick, this);
25554         
25555     },
25556     
25557     onClick : function(e)
25558     {
25559         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25560             this.setChecked(true);
25561         }
25562     },
25563     
25564     setChecked : function(state, suppressEvent)
25565     {
25566         this.parent().setValue(this.value, suppressEvent);
25567         
25568     },
25569     
25570     setBoxLabel : function(v)
25571     {
25572         this.boxLabel = v;
25573         
25574         if(this.rendered){
25575             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25576         }
25577     }
25578     
25579 });
25580  
25581
25582  /*
25583  * - LGPL
25584  *
25585  * Input
25586  * 
25587  */
25588
25589 /**
25590  * @class Roo.bootstrap.form.SecurePass
25591  * @extends Roo.bootstrap.form.Input
25592  * Bootstrap SecurePass class
25593  *
25594  * 
25595  * @constructor
25596  * Create a new SecurePass
25597  * @param {Object} config The config object
25598  */
25599  
25600 Roo.bootstrap.form.SecurePass = function (config) {
25601     // these go here, so the translation tool can replace them..
25602     this.errors = {
25603         PwdEmpty: "Please type a password, and then retype it to confirm.",
25604         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25605         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25606         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25607         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25608         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25609         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25610         TooWeak: "Your password is Too Weak."
25611     },
25612     this.meterLabel = "Password strength:";
25613     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25614     this.meterClass = [
25615         "roo-password-meter-tooweak", 
25616         "roo-password-meter-weak", 
25617         "roo-password-meter-medium", 
25618         "roo-password-meter-strong", 
25619         "roo-password-meter-grey"
25620     ];
25621     
25622     this.errors = {};
25623     
25624     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25625 }
25626
25627 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25628     /**
25629      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25630      * {
25631      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25632      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25633      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25634      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25635      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25636      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25637      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25638      * })
25639      */
25640     // private
25641     
25642     meterWidth: 300,
25643     errorMsg :'',    
25644     errors: false,
25645     imageRoot: '/',
25646     /**
25647      * @cfg {String/Object} Label for the strength meter (defaults to
25648      * 'Password strength:')
25649      */
25650     // private
25651     meterLabel: '',
25652     /**
25653      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25654      * ['Weak', 'Medium', 'Strong'])
25655      */
25656     // private    
25657     pwdStrengths: false,    
25658     // private
25659     strength: 0,
25660     // private
25661     _lastPwd: null,
25662     // private
25663     kCapitalLetter: 0,
25664     kSmallLetter: 1,
25665     kDigit: 2,
25666     kPunctuation: 3,
25667     
25668     insecure: false,
25669     // private
25670     initEvents: function ()
25671     {
25672         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25673
25674         if (this.el.is('input[type=password]') && Roo.isSafari) {
25675             this.el.on('keydown', this.SafariOnKeyDown, this);
25676         }
25677
25678         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25679     },
25680     // private
25681     onRender: function (ct, position)
25682     {
25683         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25684         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25685         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25686
25687         this.trigger.createChild({
25688                    cn: [
25689                     {
25690                     //id: 'PwdMeter',
25691                     tag: 'div',
25692                     cls: 'roo-password-meter-grey col-xs-12',
25693                     style: {
25694                         //width: 0,
25695                         //width: this.meterWidth + 'px'                                                
25696                         }
25697                     },
25698                     {                            
25699                          cls: 'roo-password-meter-text'                          
25700                     }
25701                 ]            
25702         });
25703
25704          
25705         if (this.hideTrigger) {
25706             this.trigger.setDisplayed(false);
25707         }
25708         this.setSize(this.width || '', this.height || '');
25709     },
25710     // private
25711     onDestroy: function ()
25712     {
25713         if (this.trigger) {
25714             this.trigger.removeAllListeners();
25715             this.trigger.remove();
25716         }
25717         if (this.wrap) {
25718             this.wrap.remove();
25719         }
25720         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25721     },
25722     // private
25723     checkStrength: function ()
25724     {
25725         var pwd = this.inputEl().getValue();
25726         if (pwd == this._lastPwd) {
25727             return;
25728         }
25729
25730         var strength;
25731         if (this.ClientSideStrongPassword(pwd)) {
25732             strength = 3;
25733         } else if (this.ClientSideMediumPassword(pwd)) {
25734             strength = 2;
25735         } else if (this.ClientSideWeakPassword(pwd)) {
25736             strength = 1;
25737         } else {
25738             strength = 0;
25739         }
25740         
25741         Roo.log('strength1: ' + strength);
25742         
25743         //var pm = this.trigger.child('div/div/div').dom;
25744         var pm = this.trigger.child('div/div');
25745         pm.removeClass(this.meterClass);
25746         pm.addClass(this.meterClass[strength]);
25747                 
25748         
25749         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25750                 
25751         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25752         
25753         this._lastPwd = pwd;
25754     },
25755     reset: function ()
25756     {
25757         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25758         
25759         this._lastPwd = '';
25760         
25761         var pm = this.trigger.child('div/div');
25762         pm.removeClass(this.meterClass);
25763         pm.addClass('roo-password-meter-grey');        
25764         
25765         
25766         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25767         
25768         pt.innerHTML = '';
25769         this.inputEl().dom.type='password';
25770     },
25771     // private
25772     validateValue: function (value)
25773     {
25774         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25775             return false;
25776         }
25777         if (value.length == 0) {
25778             if (this.allowBlank) {
25779                 this.clearInvalid();
25780                 return true;
25781             }
25782
25783             this.markInvalid(this.errors.PwdEmpty);
25784             this.errorMsg = this.errors.PwdEmpty;
25785             return false;
25786         }
25787         
25788         if(this.insecure){
25789             return true;
25790         }
25791         
25792         if (!value.match(/[\x21-\x7e]+/)) {
25793             this.markInvalid(this.errors.PwdBadChar);
25794             this.errorMsg = this.errors.PwdBadChar;
25795             return false;
25796         }
25797         if (value.length < 6) {
25798             this.markInvalid(this.errors.PwdShort);
25799             this.errorMsg = this.errors.PwdShort;
25800             return false;
25801         }
25802         if (value.length > 16) {
25803             this.markInvalid(this.errors.PwdLong);
25804             this.errorMsg = this.errors.PwdLong;
25805             return false;
25806         }
25807         var strength;
25808         if (this.ClientSideStrongPassword(value)) {
25809             strength = 3;
25810         } else if (this.ClientSideMediumPassword(value)) {
25811             strength = 2;
25812         } else if (this.ClientSideWeakPassword(value)) {
25813             strength = 1;
25814         } else {
25815             strength = 0;
25816         }
25817
25818         
25819         if (strength < 2) {
25820             //this.markInvalid(this.errors.TooWeak);
25821             this.errorMsg = this.errors.TooWeak;
25822             //return false;
25823         }
25824         
25825         
25826         console.log('strength2: ' + strength);
25827         
25828         //var pm = this.trigger.child('div/div/div').dom;
25829         
25830         var pm = this.trigger.child('div/div');
25831         pm.removeClass(this.meterClass);
25832         pm.addClass(this.meterClass[strength]);
25833                 
25834         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25835                 
25836         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25837         
25838         this.errorMsg = ''; 
25839         return true;
25840     },
25841     // private
25842     CharacterSetChecks: function (type)
25843     {
25844         this.type = type;
25845         this.fResult = false;
25846     },
25847     // private
25848     isctype: function (character, type)
25849     {
25850         switch (type) {  
25851             case this.kCapitalLetter:
25852                 if (character >= 'A' && character <= 'Z') {
25853                     return true;
25854                 }
25855                 break;
25856             
25857             case this.kSmallLetter:
25858                 if (character >= 'a' && character <= 'z') {
25859                     return true;
25860                 }
25861                 break;
25862             
25863             case this.kDigit:
25864                 if (character >= '0' && character <= '9') {
25865                     return true;
25866                 }
25867                 break;
25868             
25869             case this.kPunctuation:
25870                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25871                     return true;
25872                 }
25873                 break;
25874             
25875             default:
25876                 return false;
25877         }
25878
25879     },
25880     // private
25881     IsLongEnough: function (pwd, size)
25882     {
25883         return !(pwd == null || isNaN(size) || pwd.length < size);
25884     },
25885     // private
25886     SpansEnoughCharacterSets: function (word, nb)
25887     {
25888         if (!this.IsLongEnough(word, nb))
25889         {
25890             return false;
25891         }
25892
25893         var characterSetChecks = new Array(
25894             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25895             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25896         );
25897         
25898         for (var index = 0; index < word.length; ++index) {
25899             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25900                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25901                     characterSetChecks[nCharSet].fResult = true;
25902                     break;
25903                 }
25904             }
25905         }
25906
25907         var nCharSets = 0;
25908         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25909             if (characterSetChecks[nCharSet].fResult) {
25910                 ++nCharSets;
25911             }
25912         }
25913
25914         if (nCharSets < nb) {
25915             return false;
25916         }
25917         return true;
25918     },
25919     // private
25920     ClientSideStrongPassword: function (pwd)
25921     {
25922         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25923     },
25924     // private
25925     ClientSideMediumPassword: function (pwd)
25926     {
25927         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25928     },
25929     // private
25930     ClientSideWeakPassword: function (pwd)
25931     {
25932         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25933     }
25934           
25935 })//<script type="text/javascript">
25936
25937 /*
25938  * Based  Ext JS Library 1.1.1
25939  * Copyright(c) 2006-2007, Ext JS, LLC.
25940  * LGPL
25941  *
25942  */
25943  
25944 /**
25945  * @class Roo.HtmlEditorCore
25946  * @extends Roo.Component
25947  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25948  *
25949  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25950  */
25951
25952 Roo.HtmlEditorCore = function(config){
25953     
25954     
25955     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25956     
25957     
25958     this.addEvents({
25959         /**
25960          * @event initialize
25961          * Fires when the editor is fully initialized (including the iframe)
25962          * @param {Roo.HtmlEditorCore} this
25963          */
25964         initialize: true,
25965         /**
25966          * @event activate
25967          * Fires when the editor is first receives the focus. Any insertion must wait
25968          * until after this event.
25969          * @param {Roo.HtmlEditorCore} this
25970          */
25971         activate: true,
25972          /**
25973          * @event beforesync
25974          * Fires before the textarea is updated with content from the editor iframe. Return false
25975          * to cancel the sync.
25976          * @param {Roo.HtmlEditorCore} this
25977          * @param {String} html
25978          */
25979         beforesync: true,
25980          /**
25981          * @event beforepush
25982          * Fires before the iframe editor is updated with content from the textarea. Return false
25983          * to cancel the push.
25984          * @param {Roo.HtmlEditorCore} this
25985          * @param {String} html
25986          */
25987         beforepush: true,
25988          /**
25989          * @event sync
25990          * Fires when the textarea is updated with content from the editor iframe.
25991          * @param {Roo.HtmlEditorCore} this
25992          * @param {String} html
25993          */
25994         sync: true,
25995          /**
25996          * @event push
25997          * Fires when the iframe editor is updated with content from the textarea.
25998          * @param {Roo.HtmlEditorCore} this
25999          * @param {String} html
26000          */
26001         push: true,
26002         
26003         /**
26004          * @event editorevent
26005          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26006          * @param {Roo.HtmlEditorCore} this
26007          */
26008         editorevent: true
26009         
26010     });
26011     
26012     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26013     
26014     // defaults : white / black...
26015     this.applyBlacklists();
26016     
26017     
26018     
26019 };
26020
26021
26022 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26023
26024
26025      /**
26026      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26027      */
26028     
26029     owner : false,
26030     
26031      /**
26032      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26033      *                        Roo.resizable.
26034      */
26035     resizable : false,
26036      /**
26037      * @cfg {Number} height (in pixels)
26038      */   
26039     height: 300,
26040    /**
26041      * @cfg {Number} width (in pixels)
26042      */   
26043     width: 500,
26044     
26045     /**
26046      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26047      * 
26048      */
26049     stylesheets: false,
26050     
26051     /**
26052      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26053      */
26054     allowComments: false,
26055     // id of frame..
26056     frameId: false,
26057     
26058     // private properties
26059     validationEvent : false,
26060     deferHeight: true,
26061     initialized : false,
26062     activated : false,
26063     sourceEditMode : false,
26064     onFocus : Roo.emptyFn,
26065     iframePad:3,
26066     hideMode:'offsets',
26067     
26068     clearUp: true,
26069     
26070     // blacklist + whitelisted elements..
26071     black: false,
26072     white: false,
26073      
26074     bodyCls : '',
26075
26076     /**
26077      * Protected method that will not generally be called directly. It
26078      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26079      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26080      */
26081     getDocMarkup : function(){
26082         // body styles..
26083         var st = '';
26084         
26085         // inherit styels from page...?? 
26086         if (this.stylesheets === false) {
26087             
26088             Roo.get(document.head).select('style').each(function(node) {
26089                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26090             });
26091             
26092             Roo.get(document.head).select('link').each(function(node) { 
26093                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26094             });
26095             
26096         } else if (!this.stylesheets.length) {
26097                 // simple..
26098                 st = '<style type="text/css">' +
26099                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26100                    '</style>';
26101         } else {
26102             for (var i in this.stylesheets) { 
26103                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26104             }
26105             
26106         }
26107         
26108         st +=  '<style type="text/css">' +
26109             'IMG { cursor: pointer } ' +
26110         '</style>';
26111
26112         var cls = 'roo-htmleditor-body';
26113         
26114         if(this.bodyCls.length){
26115             cls += ' ' + this.bodyCls;
26116         }
26117         
26118         return '<html><head>' + st  +
26119             //<style type="text/css">' +
26120             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26121             //'</style>' +
26122             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26123     },
26124
26125     // private
26126     onRender : function(ct, position)
26127     {
26128         var _t = this;
26129         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26130         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26131         
26132         
26133         this.el.dom.style.border = '0 none';
26134         this.el.dom.setAttribute('tabIndex', -1);
26135         this.el.addClass('x-hidden hide');
26136         
26137         
26138         
26139         if(Roo.isIE){ // fix IE 1px bogus margin
26140             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26141         }
26142        
26143         
26144         this.frameId = Roo.id();
26145         
26146          
26147         
26148         var iframe = this.owner.wrap.createChild({
26149             tag: 'iframe',
26150             cls: 'form-control', // bootstrap..
26151             id: this.frameId,
26152             name: this.frameId,
26153             frameBorder : 'no',
26154             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26155         }, this.el
26156         );
26157         
26158         
26159         this.iframe = iframe.dom;
26160
26161          this.assignDocWin();
26162         
26163         this.doc.designMode = 'on';
26164        
26165         this.doc.open();
26166         this.doc.write(this.getDocMarkup());
26167         this.doc.close();
26168
26169         
26170         var task = { // must defer to wait for browser to be ready
26171             run : function(){
26172                 //console.log("run task?" + this.doc.readyState);
26173                 this.assignDocWin();
26174                 if(this.doc.body || this.doc.readyState == 'complete'){
26175                     try {
26176                         this.doc.designMode="on";
26177                     } catch (e) {
26178                         return;
26179                     }
26180                     Roo.TaskMgr.stop(task);
26181                     this.initEditor.defer(10, this);
26182                 }
26183             },
26184             interval : 10,
26185             duration: 10000,
26186             scope: this
26187         };
26188         Roo.TaskMgr.start(task);
26189
26190     },
26191
26192     // private
26193     onResize : function(w, h)
26194     {
26195          Roo.log('resize: ' +w + ',' + h );
26196         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26197         if(!this.iframe){
26198             return;
26199         }
26200         if(typeof w == 'number'){
26201             
26202             this.iframe.style.width = w + 'px';
26203         }
26204         if(typeof h == 'number'){
26205             
26206             this.iframe.style.height = h + 'px';
26207             if(this.doc){
26208                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26209             }
26210         }
26211         
26212     },
26213
26214     /**
26215      * Toggles the editor between standard and source edit mode.
26216      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26217      */
26218     toggleSourceEdit : function(sourceEditMode){
26219         
26220         this.sourceEditMode = sourceEditMode === true;
26221         
26222         if(this.sourceEditMode){
26223  
26224             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26225             
26226         }else{
26227             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26228             //this.iframe.className = '';
26229             this.deferFocus();
26230         }
26231         //this.setSize(this.owner.wrap.getSize());
26232         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26233     },
26234
26235     
26236   
26237
26238     /**
26239      * Protected method that will not generally be called directly. If you need/want
26240      * custom HTML cleanup, this is the method you should override.
26241      * @param {String} html The HTML to be cleaned
26242      * return {String} The cleaned HTML
26243      */
26244     cleanHtml : function(html){
26245         html = String(html);
26246         if(html.length > 5){
26247             if(Roo.isSafari){ // strip safari nonsense
26248                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26249             }
26250         }
26251         if(html == '&nbsp;'){
26252             html = '';
26253         }
26254         return html;
26255     },
26256
26257     /**
26258      * HTML Editor -> Textarea
26259      * Protected method that will not generally be called directly. Syncs the contents
26260      * of the editor iframe with the textarea.
26261      */
26262     syncValue : function(){
26263         if(this.initialized){
26264             var bd = (this.doc.body || this.doc.documentElement);
26265             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26266             var html = bd.innerHTML;
26267             if(Roo.isSafari){
26268                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26269                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26270                 if(m && m[1]){
26271                     html = '<div style="'+m[0]+'">' + html + '</div>';
26272                 }
26273             }
26274             html = this.cleanHtml(html);
26275             // fix up the special chars.. normaly like back quotes in word...
26276             // however we do not want to do this with chinese..
26277             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26278                 
26279                 var cc = match.charCodeAt();
26280
26281                 // Get the character value, handling surrogate pairs
26282                 if (match.length == 2) {
26283                     // It's a surrogate pair, calculate the Unicode code point
26284                     var high = match.charCodeAt(0) - 0xD800;
26285                     var low  = match.charCodeAt(1) - 0xDC00;
26286                     cc = (high * 0x400) + low + 0x10000;
26287                 }  else if (
26288                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26289                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26290                     (cc >= 0xf900 && cc < 0xfb00 )
26291                 ) {
26292                         return match;
26293                 }  
26294          
26295                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26296                 return "&#" + cc + ";";
26297                 
26298                 
26299             });
26300             
26301             
26302              
26303             if(this.owner.fireEvent('beforesync', this, html) !== false){
26304                 this.el.dom.value = html;
26305                 this.owner.fireEvent('sync', this, html);
26306             }
26307         }
26308     },
26309
26310     /**
26311      * Protected method that will not generally be called directly. Pushes the value of the textarea
26312      * into the iframe editor.
26313      */
26314     pushValue : function(){
26315         if(this.initialized){
26316             var v = this.el.dom.value.trim();
26317             
26318 //            if(v.length < 1){
26319 //                v = '&#160;';
26320 //            }
26321             
26322             if(this.owner.fireEvent('beforepush', this, v) !== false){
26323                 var d = (this.doc.body || this.doc.documentElement);
26324                 d.innerHTML = v;
26325                 this.cleanUpPaste();
26326                 this.el.dom.value = d.innerHTML;
26327                 this.owner.fireEvent('push', this, v);
26328             }
26329         }
26330     },
26331
26332     // private
26333     deferFocus : function(){
26334         this.focus.defer(10, this);
26335     },
26336
26337     // doc'ed in Field
26338     focus : function(){
26339         if(this.win && !this.sourceEditMode){
26340             this.win.focus();
26341         }else{
26342             this.el.focus();
26343         }
26344     },
26345     
26346     assignDocWin: function()
26347     {
26348         var iframe = this.iframe;
26349         
26350          if(Roo.isIE){
26351             this.doc = iframe.contentWindow.document;
26352             this.win = iframe.contentWindow;
26353         } else {
26354 //            if (!Roo.get(this.frameId)) {
26355 //                return;
26356 //            }
26357 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26358 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26359             
26360             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26361                 return;
26362             }
26363             
26364             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26365             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26366         }
26367     },
26368     
26369     // private
26370     initEditor : function(){
26371         //console.log("INIT EDITOR");
26372         this.assignDocWin();
26373         
26374         
26375         
26376         this.doc.designMode="on";
26377         this.doc.open();
26378         this.doc.write(this.getDocMarkup());
26379         this.doc.close();
26380         
26381         var dbody = (this.doc.body || this.doc.documentElement);
26382         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26383         // this copies styles from the containing element into thsi one..
26384         // not sure why we need all of this..
26385         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26386         
26387         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26388         //ss['background-attachment'] = 'fixed'; // w3c
26389         dbody.bgProperties = 'fixed'; // ie
26390         //Roo.DomHelper.applyStyles(dbody, ss);
26391         Roo.EventManager.on(this.doc, {
26392             //'mousedown': this.onEditorEvent,
26393             'mouseup': this.onEditorEvent,
26394             'dblclick': this.onEditorEvent,
26395             'click': this.onEditorEvent,
26396             'keyup': this.onEditorEvent,
26397             buffer:100,
26398             scope: this
26399         });
26400         if(Roo.isGecko){
26401             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26402         }
26403         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26404             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26405         }
26406         this.initialized = true;
26407
26408         this.owner.fireEvent('initialize', this);
26409         this.pushValue();
26410     },
26411
26412     // private
26413     onDestroy : function(){
26414         
26415         
26416         
26417         if(this.rendered){
26418             
26419             //for (var i =0; i < this.toolbars.length;i++) {
26420             //    // fixme - ask toolbars for heights?
26421             //    this.toolbars[i].onDestroy();
26422            // }
26423             
26424             //this.wrap.dom.innerHTML = '';
26425             //this.wrap.remove();
26426         }
26427     },
26428
26429     // private
26430     onFirstFocus : function(){
26431         
26432         this.assignDocWin();
26433         
26434         
26435         this.activated = true;
26436          
26437     
26438         if(Roo.isGecko){ // prevent silly gecko errors
26439             this.win.focus();
26440             var s = this.win.getSelection();
26441             if(!s.focusNode || s.focusNode.nodeType != 3){
26442                 var r = s.getRangeAt(0);
26443                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26444                 r.collapse(true);
26445                 this.deferFocus();
26446             }
26447             try{
26448                 this.execCmd('useCSS', true);
26449                 this.execCmd('styleWithCSS', false);
26450             }catch(e){}
26451         }
26452         this.owner.fireEvent('activate', this);
26453     },
26454
26455     // private
26456     adjustFont: function(btn){
26457         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26458         //if(Roo.isSafari){ // safari
26459         //    adjust *= 2;
26460        // }
26461         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26462         if(Roo.isSafari){ // safari
26463             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26464             v =  (v < 10) ? 10 : v;
26465             v =  (v > 48) ? 48 : v;
26466             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26467             
26468         }
26469         
26470         
26471         v = Math.max(1, v+adjust);
26472         
26473         this.execCmd('FontSize', v  );
26474     },
26475
26476     onEditorEvent : function(e)
26477     {
26478         this.owner.fireEvent('editorevent', this, e);
26479       //  this.updateToolbar();
26480         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26481     },
26482
26483     insertTag : function(tg)
26484     {
26485         // could be a bit smarter... -> wrap the current selected tRoo..
26486         if (tg.toLowerCase() == 'span' ||
26487             tg.toLowerCase() == 'code' ||
26488             tg.toLowerCase() == 'sup' ||
26489             tg.toLowerCase() == 'sub' 
26490             ) {
26491             
26492             range = this.createRange(this.getSelection());
26493             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26494             wrappingNode.appendChild(range.extractContents());
26495             range.insertNode(wrappingNode);
26496
26497             return;
26498             
26499             
26500             
26501         }
26502         this.execCmd("formatblock",   tg);
26503         
26504     },
26505     
26506     insertText : function(txt)
26507     {
26508         
26509         
26510         var range = this.createRange();
26511         range.deleteContents();
26512                //alert(Sender.getAttribute('label'));
26513                
26514         range.insertNode(this.doc.createTextNode(txt));
26515     } ,
26516     
26517      
26518
26519     /**
26520      * Executes a Midas editor command on the editor document and performs necessary focus and
26521      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26522      * @param {String} cmd The Midas command
26523      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26524      */
26525     relayCmd : function(cmd, value){
26526         this.win.focus();
26527         this.execCmd(cmd, value);
26528         this.owner.fireEvent('editorevent', this);
26529         //this.updateToolbar();
26530         this.owner.deferFocus();
26531     },
26532
26533     /**
26534      * Executes a Midas editor command directly on the editor document.
26535      * For visual commands, you should use {@link #relayCmd} instead.
26536      * <b>This should only be called after the editor is initialized.</b>
26537      * @param {String} cmd The Midas command
26538      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26539      */
26540     execCmd : function(cmd, value){
26541         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26542         this.syncValue();
26543     },
26544  
26545  
26546    
26547     /**
26548      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26549      * to insert tRoo.
26550      * @param {String} text | dom node.. 
26551      */
26552     insertAtCursor : function(text)
26553     {
26554         
26555         if(!this.activated){
26556             return;
26557         }
26558         /*
26559         if(Roo.isIE){
26560             this.win.focus();
26561             var r = this.doc.selection.createRange();
26562             if(r){
26563                 r.collapse(true);
26564                 r.pasteHTML(text);
26565                 this.syncValue();
26566                 this.deferFocus();
26567             
26568             }
26569             return;
26570         }
26571         */
26572         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26573             this.win.focus();
26574             
26575             
26576             // from jquery ui (MIT licenced)
26577             var range, node;
26578             var win = this.win;
26579             
26580             if (win.getSelection && win.getSelection().getRangeAt) {
26581                 range = win.getSelection().getRangeAt(0);
26582                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26583                 range.insertNode(node);
26584             } else if (win.document.selection && win.document.selection.createRange) {
26585                 // no firefox support
26586                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26587                 win.document.selection.createRange().pasteHTML(txt);
26588             } else {
26589                 // no firefox support
26590                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26591                 this.execCmd('InsertHTML', txt);
26592             } 
26593             
26594             this.syncValue();
26595             
26596             this.deferFocus();
26597         }
26598     },
26599  // private
26600     mozKeyPress : function(e){
26601         if(e.ctrlKey){
26602             var c = e.getCharCode(), cmd;
26603           
26604             if(c > 0){
26605                 c = String.fromCharCode(c).toLowerCase();
26606                 switch(c){
26607                     case 'b':
26608                         cmd = 'bold';
26609                         break;
26610                     case 'i':
26611                         cmd = 'italic';
26612                         break;
26613                     
26614                     case 'u':
26615                         cmd = 'underline';
26616                         break;
26617                     
26618                     case 'v':
26619                         this.cleanUpPaste.defer(100, this);
26620                         return;
26621                         
26622                 }
26623                 if(cmd){
26624                     this.win.focus();
26625                     this.execCmd(cmd);
26626                     this.deferFocus();
26627                     e.preventDefault();
26628                 }
26629                 
26630             }
26631         }
26632     },
26633
26634     // private
26635     fixKeys : function(){ // load time branching for fastest keydown performance
26636         if(Roo.isIE){
26637             return function(e){
26638                 var k = e.getKey(), r;
26639                 if(k == e.TAB){
26640                     e.stopEvent();
26641                     r = this.doc.selection.createRange();
26642                     if(r){
26643                         r.collapse(true);
26644                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26645                         this.deferFocus();
26646                     }
26647                     return;
26648                 }
26649                 
26650                 if(k == e.ENTER){
26651                     r = this.doc.selection.createRange();
26652                     if(r){
26653                         var target = r.parentElement();
26654                         if(!target || target.tagName.toLowerCase() != 'li'){
26655                             e.stopEvent();
26656                             r.pasteHTML('<br />');
26657                             r.collapse(false);
26658                             r.select();
26659                         }
26660                     }
26661                 }
26662                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26663                     this.cleanUpPaste.defer(100, this);
26664                     return;
26665                 }
26666                 
26667                 
26668             };
26669         }else if(Roo.isOpera){
26670             return function(e){
26671                 var k = e.getKey();
26672                 if(k == e.TAB){
26673                     e.stopEvent();
26674                     this.win.focus();
26675                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26676                     this.deferFocus();
26677                 }
26678                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26679                     this.cleanUpPaste.defer(100, this);
26680                     return;
26681                 }
26682                 
26683             };
26684         }else if(Roo.isSafari){
26685             return function(e){
26686                 var k = e.getKey();
26687                 
26688                 if(k == e.TAB){
26689                     e.stopEvent();
26690                     this.execCmd('InsertText','\t');
26691                     this.deferFocus();
26692                     return;
26693                 }
26694                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26695                     this.cleanUpPaste.defer(100, this);
26696                     return;
26697                 }
26698                 
26699              };
26700         }
26701     }(),
26702     
26703     getAllAncestors: function()
26704     {
26705         var p = this.getSelectedNode();
26706         var a = [];
26707         if (!p) {
26708             a.push(p); // push blank onto stack..
26709             p = this.getParentElement();
26710         }
26711         
26712         
26713         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26714             a.push(p);
26715             p = p.parentNode;
26716         }
26717         a.push(this.doc.body);
26718         return a;
26719     },
26720     lastSel : false,
26721     lastSelNode : false,
26722     
26723     
26724     getSelection : function() 
26725     {
26726         this.assignDocWin();
26727         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26728     },
26729     
26730     getSelectedNode: function() 
26731     {
26732         // this may only work on Gecko!!!
26733         
26734         // should we cache this!!!!
26735         
26736         
26737         
26738          
26739         var range = this.createRange(this.getSelection()).cloneRange();
26740         
26741         if (Roo.isIE) {
26742             var parent = range.parentElement();
26743             while (true) {
26744                 var testRange = range.duplicate();
26745                 testRange.moveToElementText(parent);
26746                 if (testRange.inRange(range)) {
26747                     break;
26748                 }
26749                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26750                     break;
26751                 }
26752                 parent = parent.parentElement;
26753             }
26754             return parent;
26755         }
26756         
26757         // is ancestor a text element.
26758         var ac =  range.commonAncestorContainer;
26759         if (ac.nodeType == 3) {
26760             ac = ac.parentNode;
26761         }
26762         
26763         var ar = ac.childNodes;
26764          
26765         var nodes = [];
26766         var other_nodes = [];
26767         var has_other_nodes = false;
26768         for (var i=0;i<ar.length;i++) {
26769             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26770                 continue;
26771             }
26772             // fullly contained node.
26773             
26774             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26775                 nodes.push(ar[i]);
26776                 continue;
26777             }
26778             
26779             // probably selected..
26780             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26781                 other_nodes.push(ar[i]);
26782                 continue;
26783             }
26784             // outer..
26785             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26786                 continue;
26787             }
26788             
26789             
26790             has_other_nodes = true;
26791         }
26792         if (!nodes.length && other_nodes.length) {
26793             nodes= other_nodes;
26794         }
26795         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26796             return false;
26797         }
26798         
26799         return nodes[0];
26800     },
26801     createRange: function(sel)
26802     {
26803         // this has strange effects when using with 
26804         // top toolbar - not sure if it's a great idea.
26805         //this.editor.contentWindow.focus();
26806         if (typeof sel != "undefined") {
26807             try {
26808                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26809             } catch(e) {
26810                 return this.doc.createRange();
26811             }
26812         } else {
26813             return this.doc.createRange();
26814         }
26815     },
26816     getParentElement: function()
26817     {
26818         
26819         this.assignDocWin();
26820         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26821         
26822         var range = this.createRange(sel);
26823          
26824         try {
26825             var p = range.commonAncestorContainer;
26826             while (p.nodeType == 3) { // text node
26827                 p = p.parentNode;
26828             }
26829             return p;
26830         } catch (e) {
26831             return null;
26832         }
26833     
26834     },
26835     /***
26836      *
26837      * Range intersection.. the hard stuff...
26838      *  '-1' = before
26839      *  '0' = hits..
26840      *  '1' = after.
26841      *         [ -- selected range --- ]
26842      *   [fail]                        [fail]
26843      *
26844      *    basically..
26845      *      if end is before start or  hits it. fail.
26846      *      if start is after end or hits it fail.
26847      *
26848      *   if either hits (but other is outside. - then it's not 
26849      *   
26850      *    
26851      **/
26852     
26853     
26854     // @see http://www.thismuchiknow.co.uk/?p=64.
26855     rangeIntersectsNode : function(range, node)
26856     {
26857         var nodeRange = node.ownerDocument.createRange();
26858         try {
26859             nodeRange.selectNode(node);
26860         } catch (e) {
26861             nodeRange.selectNodeContents(node);
26862         }
26863     
26864         var rangeStartRange = range.cloneRange();
26865         rangeStartRange.collapse(true);
26866     
26867         var rangeEndRange = range.cloneRange();
26868         rangeEndRange.collapse(false);
26869     
26870         var nodeStartRange = nodeRange.cloneRange();
26871         nodeStartRange.collapse(true);
26872     
26873         var nodeEndRange = nodeRange.cloneRange();
26874         nodeEndRange.collapse(false);
26875     
26876         return rangeStartRange.compareBoundaryPoints(
26877                  Range.START_TO_START, nodeEndRange) == -1 &&
26878                rangeEndRange.compareBoundaryPoints(
26879                  Range.START_TO_START, nodeStartRange) == 1;
26880         
26881          
26882     },
26883     rangeCompareNode : function(range, node)
26884     {
26885         var nodeRange = node.ownerDocument.createRange();
26886         try {
26887             nodeRange.selectNode(node);
26888         } catch (e) {
26889             nodeRange.selectNodeContents(node);
26890         }
26891         
26892         
26893         range.collapse(true);
26894     
26895         nodeRange.collapse(true);
26896      
26897         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26898         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26899          
26900         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26901         
26902         var nodeIsBefore   =  ss == 1;
26903         var nodeIsAfter    = ee == -1;
26904         
26905         if (nodeIsBefore && nodeIsAfter) {
26906             return 0; // outer
26907         }
26908         if (!nodeIsBefore && nodeIsAfter) {
26909             return 1; //right trailed.
26910         }
26911         
26912         if (nodeIsBefore && !nodeIsAfter) {
26913             return 2;  // left trailed.
26914         }
26915         // fully contined.
26916         return 3;
26917     },
26918
26919     // private? - in a new class?
26920     cleanUpPaste :  function()
26921     {
26922         // cleans up the whole document..
26923         Roo.log('cleanuppaste');
26924         
26925         this.cleanUpChildren(this.doc.body);
26926         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26927         if (clean != this.doc.body.innerHTML) {
26928             this.doc.body.innerHTML = clean;
26929         }
26930         
26931     },
26932     
26933     cleanWordChars : function(input) {// change the chars to hex code
26934         var he = Roo.HtmlEditorCore;
26935         
26936         var output = input;
26937         Roo.each(he.swapCodes, function(sw) { 
26938             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26939             
26940             output = output.replace(swapper, sw[1]);
26941         });
26942         
26943         return output;
26944     },
26945     
26946     
26947     cleanUpChildren : function (n)
26948     {
26949         if (!n.childNodes.length) {
26950             return;
26951         }
26952         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26953            this.cleanUpChild(n.childNodes[i]);
26954         }
26955     },
26956     
26957     
26958         
26959     
26960     cleanUpChild : function (node)
26961     {
26962         var ed = this;
26963         //console.log(node);
26964         if (node.nodeName == "#text") {
26965             // clean up silly Windows -- stuff?
26966             return; 
26967         }
26968         if (node.nodeName == "#comment") {
26969             if (!this.allowComments) {
26970                 node.parentNode.removeChild(node);
26971             }
26972             // clean up silly Windows -- stuff?
26973             return; 
26974         }
26975         var lcname = node.tagName.toLowerCase();
26976         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26977         // whitelist of tags..
26978         
26979         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26980             // remove node.
26981             node.parentNode.removeChild(node);
26982             return;
26983             
26984         }
26985         
26986         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26987         
26988         // spans with no attributes - just remove them..
26989         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26990             remove_keep_children = true;
26991         }
26992         
26993         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26994         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26995         
26996         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26997         //    remove_keep_children = true;
26998         //}
26999         
27000         if (remove_keep_children) {
27001             this.cleanUpChildren(node);
27002             // inserts everything just before this node...
27003             while (node.childNodes.length) {
27004                 var cn = node.childNodes[0];
27005                 node.removeChild(cn);
27006                 node.parentNode.insertBefore(cn, node);
27007             }
27008             node.parentNode.removeChild(node);
27009             return;
27010         }
27011         
27012         if (!node.attributes || !node.attributes.length) {
27013             
27014           
27015             
27016             
27017             this.cleanUpChildren(node);
27018             return;
27019         }
27020         
27021         function cleanAttr(n,v)
27022         {
27023             
27024             if (v.match(/^\./) || v.match(/^\//)) {
27025                 return;
27026             }
27027             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27028                 return;
27029             }
27030             if (v.match(/^#/)) {
27031                 return;
27032             }
27033             if (v.match(/^\{/)) { // allow template editing.
27034                 return;
27035             }
27036 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27037             node.removeAttribute(n);
27038             
27039         }
27040         
27041         var cwhite = this.cwhite;
27042         var cblack = this.cblack;
27043             
27044         function cleanStyle(n,v)
27045         {
27046             if (v.match(/expression/)) { //XSS?? should we even bother..
27047                 node.removeAttribute(n);
27048                 return;
27049             }
27050             
27051             var parts = v.split(/;/);
27052             var clean = [];
27053             
27054             Roo.each(parts, function(p) {
27055                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27056                 if (!p.length) {
27057                     return true;
27058                 }
27059                 var l = p.split(':').shift().replace(/\s+/g,'');
27060                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27061                 
27062                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27063 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27064                     //node.removeAttribute(n);
27065                     return true;
27066                 }
27067                 //Roo.log()
27068                 // only allow 'c whitelisted system attributes'
27069                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27070 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27071                     //node.removeAttribute(n);
27072                     return true;
27073                 }
27074                 
27075                 
27076                  
27077                 
27078                 clean.push(p);
27079                 return true;
27080             });
27081             if (clean.length) { 
27082                 node.setAttribute(n, clean.join(';'));
27083             } else {
27084                 node.removeAttribute(n);
27085             }
27086             
27087         }
27088         
27089         
27090         for (var i = node.attributes.length-1; i > -1 ; i--) {
27091             var a = node.attributes[i];
27092             //console.log(a);
27093             
27094             if (a.name.toLowerCase().substr(0,2)=='on')  {
27095                 node.removeAttribute(a.name);
27096                 continue;
27097             }
27098             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27099                 node.removeAttribute(a.name);
27100                 continue;
27101             }
27102             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27103                 cleanAttr(a.name,a.value); // fixme..
27104                 continue;
27105             }
27106             if (a.name == 'style') {
27107                 cleanStyle(a.name,a.value);
27108                 continue;
27109             }
27110             /// clean up MS crap..
27111             // tecnically this should be a list of valid class'es..
27112             
27113             
27114             if (a.name == 'class') {
27115                 if (a.value.match(/^Mso/)) {
27116                     node.removeAttribute('class');
27117                 }
27118                 
27119                 if (a.value.match(/^body$/)) {
27120                     node.removeAttribute('class');
27121                 }
27122                 continue;
27123             }
27124             
27125             // style cleanup!?
27126             // class cleanup?
27127             
27128         }
27129         
27130         
27131         this.cleanUpChildren(node);
27132         
27133         
27134     },
27135     
27136     /**
27137      * Clean up MS wordisms...
27138      */
27139     cleanWord : function(node)
27140     {
27141         if (!node) {
27142             this.cleanWord(this.doc.body);
27143             return;
27144         }
27145         
27146         if(
27147                 node.nodeName == 'SPAN' &&
27148                 !node.hasAttributes() &&
27149                 node.childNodes.length == 1 &&
27150                 node.firstChild.nodeName == "#text"  
27151         ) {
27152             var textNode = node.firstChild;
27153             node.removeChild(textNode);
27154             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27155                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27156             }
27157             node.parentNode.insertBefore(textNode, node);
27158             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27159                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27160             }
27161             node.parentNode.removeChild(node);
27162         }
27163         
27164         if (node.nodeName == "#text") {
27165             // clean up silly Windows -- stuff?
27166             return; 
27167         }
27168         if (node.nodeName == "#comment") {
27169             node.parentNode.removeChild(node);
27170             // clean up silly Windows -- stuff?
27171             return; 
27172         }
27173         
27174         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27175             node.parentNode.removeChild(node);
27176             return;
27177         }
27178         //Roo.log(node.tagName);
27179         // remove - but keep children..
27180         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27181             //Roo.log('-- removed');
27182             while (node.childNodes.length) {
27183                 var cn = node.childNodes[0];
27184                 node.removeChild(cn);
27185                 node.parentNode.insertBefore(cn, node);
27186                 // move node to parent - and clean it..
27187                 this.cleanWord(cn);
27188             }
27189             node.parentNode.removeChild(node);
27190             /// no need to iterate chidlren = it's got none..
27191             //this.iterateChildren(node, this.cleanWord);
27192             return;
27193         }
27194         // clean styles
27195         if (node.className.length) {
27196             
27197             var cn = node.className.split(/\W+/);
27198             var cna = [];
27199             Roo.each(cn, function(cls) {
27200                 if (cls.match(/Mso[a-zA-Z]+/)) {
27201                     return;
27202                 }
27203                 cna.push(cls);
27204             });
27205             node.className = cna.length ? cna.join(' ') : '';
27206             if (!cna.length) {
27207                 node.removeAttribute("class");
27208             }
27209         }
27210         
27211         if (node.hasAttribute("lang")) {
27212             node.removeAttribute("lang");
27213         }
27214         
27215         if (node.hasAttribute("style")) {
27216             
27217             var styles = node.getAttribute("style").split(";");
27218             var nstyle = [];
27219             Roo.each(styles, function(s) {
27220                 if (!s.match(/:/)) {
27221                     return;
27222                 }
27223                 var kv = s.split(":");
27224                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27225                     return;
27226                 }
27227                 // what ever is left... we allow.
27228                 nstyle.push(s);
27229             });
27230             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27231             if (!nstyle.length) {
27232                 node.removeAttribute('style');
27233             }
27234         }
27235         this.iterateChildren(node, this.cleanWord);
27236         
27237         
27238         
27239     },
27240     /**
27241      * iterateChildren of a Node, calling fn each time, using this as the scole..
27242      * @param {DomNode} node node to iterate children of.
27243      * @param {Function} fn method of this class to call on each item.
27244      */
27245     iterateChildren : function(node, fn)
27246     {
27247         if (!node.childNodes.length) {
27248                 return;
27249         }
27250         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27251            fn.call(this, node.childNodes[i])
27252         }
27253     },
27254     
27255     
27256     /**
27257      * cleanTableWidths.
27258      *
27259      * Quite often pasting from word etc.. results in tables with column and widths.
27260      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27261      *
27262      */
27263     cleanTableWidths : function(node)
27264     {
27265          
27266          
27267         if (!node) {
27268             this.cleanTableWidths(this.doc.body);
27269             return;
27270         }
27271         
27272         // ignore list...
27273         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27274             return; 
27275         }
27276         Roo.log(node.tagName);
27277         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27278             this.iterateChildren(node, this.cleanTableWidths);
27279             return;
27280         }
27281         if (node.hasAttribute('width')) {
27282             node.removeAttribute('width');
27283         }
27284         
27285          
27286         if (node.hasAttribute("style")) {
27287             // pretty basic...
27288             
27289             var styles = node.getAttribute("style").split(";");
27290             var nstyle = [];
27291             Roo.each(styles, function(s) {
27292                 if (!s.match(/:/)) {
27293                     return;
27294                 }
27295                 var kv = s.split(":");
27296                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27297                     return;
27298                 }
27299                 // what ever is left... we allow.
27300                 nstyle.push(s);
27301             });
27302             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27303             if (!nstyle.length) {
27304                 node.removeAttribute('style');
27305             }
27306         }
27307         
27308         this.iterateChildren(node, this.cleanTableWidths);
27309         
27310         
27311     },
27312     
27313     
27314     
27315     
27316     domToHTML : function(currentElement, depth, nopadtext) {
27317         
27318         depth = depth || 0;
27319         nopadtext = nopadtext || false;
27320     
27321         if (!currentElement) {
27322             return this.domToHTML(this.doc.body);
27323         }
27324         
27325         //Roo.log(currentElement);
27326         var j;
27327         var allText = false;
27328         var nodeName = currentElement.nodeName;
27329         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27330         
27331         if  (nodeName == '#text') {
27332             
27333             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27334         }
27335         
27336         
27337         var ret = '';
27338         if (nodeName != 'BODY') {
27339              
27340             var i = 0;
27341             // Prints the node tagName, such as <A>, <IMG>, etc
27342             if (tagName) {
27343                 var attr = [];
27344                 for(i = 0; i < currentElement.attributes.length;i++) {
27345                     // quoting?
27346                     var aname = currentElement.attributes.item(i).name;
27347                     if (!currentElement.attributes.item(i).value.length) {
27348                         continue;
27349                     }
27350                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27351                 }
27352                 
27353                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27354             } 
27355             else {
27356                 
27357                 // eack
27358             }
27359         } else {
27360             tagName = false;
27361         }
27362         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27363             return ret;
27364         }
27365         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27366             nopadtext = true;
27367         }
27368         
27369         
27370         // Traverse the tree
27371         i = 0;
27372         var currentElementChild = currentElement.childNodes.item(i);
27373         var allText = true;
27374         var innerHTML  = '';
27375         lastnode = '';
27376         while (currentElementChild) {
27377             // Formatting code (indent the tree so it looks nice on the screen)
27378             var nopad = nopadtext;
27379             if (lastnode == 'SPAN') {
27380                 nopad  = true;
27381             }
27382             // text
27383             if  (currentElementChild.nodeName == '#text') {
27384                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27385                 toadd = nopadtext ? toadd : toadd.trim();
27386                 if (!nopad && toadd.length > 80) {
27387                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27388                 }
27389                 innerHTML  += toadd;
27390                 
27391                 i++;
27392                 currentElementChild = currentElement.childNodes.item(i);
27393                 lastNode = '';
27394                 continue;
27395             }
27396             allText = false;
27397             
27398             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27399                 
27400             // Recursively traverse the tree structure of the child node
27401             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27402             lastnode = currentElementChild.nodeName;
27403             i++;
27404             currentElementChild=currentElement.childNodes.item(i);
27405         }
27406         
27407         ret += innerHTML;
27408         
27409         if (!allText) {
27410                 // The remaining code is mostly for formatting the tree
27411             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27412         }
27413         
27414         
27415         if (tagName) {
27416             ret+= "</"+tagName+">";
27417         }
27418         return ret;
27419         
27420     },
27421         
27422     applyBlacklists : function()
27423     {
27424         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27425         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27426         
27427         this.white = [];
27428         this.black = [];
27429         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27430             if (b.indexOf(tag) > -1) {
27431                 return;
27432             }
27433             this.white.push(tag);
27434             
27435         }, this);
27436         
27437         Roo.each(w, function(tag) {
27438             if (b.indexOf(tag) > -1) {
27439                 return;
27440             }
27441             if (this.white.indexOf(tag) > -1) {
27442                 return;
27443             }
27444             this.white.push(tag);
27445             
27446         }, this);
27447         
27448         
27449         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27450             if (w.indexOf(tag) > -1) {
27451                 return;
27452             }
27453             this.black.push(tag);
27454             
27455         }, this);
27456         
27457         Roo.each(b, function(tag) {
27458             if (w.indexOf(tag) > -1) {
27459                 return;
27460             }
27461             if (this.black.indexOf(tag) > -1) {
27462                 return;
27463             }
27464             this.black.push(tag);
27465             
27466         }, this);
27467         
27468         
27469         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27470         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27471         
27472         this.cwhite = [];
27473         this.cblack = [];
27474         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27475             if (b.indexOf(tag) > -1) {
27476                 return;
27477             }
27478             this.cwhite.push(tag);
27479             
27480         }, this);
27481         
27482         Roo.each(w, function(tag) {
27483             if (b.indexOf(tag) > -1) {
27484                 return;
27485             }
27486             if (this.cwhite.indexOf(tag) > -1) {
27487                 return;
27488             }
27489             this.cwhite.push(tag);
27490             
27491         }, this);
27492         
27493         
27494         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27495             if (w.indexOf(tag) > -1) {
27496                 return;
27497             }
27498             this.cblack.push(tag);
27499             
27500         }, this);
27501         
27502         Roo.each(b, function(tag) {
27503             if (w.indexOf(tag) > -1) {
27504                 return;
27505             }
27506             if (this.cblack.indexOf(tag) > -1) {
27507                 return;
27508             }
27509             this.cblack.push(tag);
27510             
27511         }, this);
27512     },
27513     
27514     setStylesheets : function(stylesheets)
27515     {
27516         if(typeof(stylesheets) == 'string'){
27517             Roo.get(this.iframe.contentDocument.head).createChild({
27518                 tag : 'link',
27519                 rel : 'stylesheet',
27520                 type : 'text/css',
27521                 href : stylesheets
27522             });
27523             
27524             return;
27525         }
27526         var _this = this;
27527      
27528         Roo.each(stylesheets, function(s) {
27529             if(!s.length){
27530                 return;
27531             }
27532             
27533             Roo.get(_this.iframe.contentDocument.head).createChild({
27534                 tag : 'link',
27535                 rel : 'stylesheet',
27536                 type : 'text/css',
27537                 href : s
27538             });
27539         });
27540
27541         
27542     },
27543     
27544     removeStylesheets : function()
27545     {
27546         var _this = this;
27547         
27548         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27549             s.remove();
27550         });
27551     },
27552     
27553     setStyle : function(style)
27554     {
27555         Roo.get(this.iframe.contentDocument.head).createChild({
27556             tag : 'style',
27557             type : 'text/css',
27558             html : style
27559         });
27560
27561         return;
27562     }
27563     
27564     // hide stuff that is not compatible
27565     /**
27566      * @event blur
27567      * @hide
27568      */
27569     /**
27570      * @event change
27571      * @hide
27572      */
27573     /**
27574      * @event focus
27575      * @hide
27576      */
27577     /**
27578      * @event specialkey
27579      * @hide
27580      */
27581     /**
27582      * @cfg {String} fieldClass @hide
27583      */
27584     /**
27585      * @cfg {String} focusClass @hide
27586      */
27587     /**
27588      * @cfg {String} autoCreate @hide
27589      */
27590     /**
27591      * @cfg {String} inputType @hide
27592      */
27593     /**
27594      * @cfg {String} invalidClass @hide
27595      */
27596     /**
27597      * @cfg {String} invalidText @hide
27598      */
27599     /**
27600      * @cfg {String} msgFx @hide
27601      */
27602     /**
27603      * @cfg {String} validateOnBlur @hide
27604      */
27605 });
27606
27607 Roo.HtmlEditorCore.white = [
27608         'area', 'br', 'img', 'input', 'hr', 'wbr',
27609         
27610        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27611        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27612        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27613        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27614        'table',   'ul',         'xmp', 
27615        
27616        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27617       'thead',   'tr', 
27618      
27619       'dir', 'menu', 'ol', 'ul', 'dl',
27620        
27621       'embed',  'object'
27622 ];
27623
27624
27625 Roo.HtmlEditorCore.black = [
27626     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27627         'applet', // 
27628         'base',   'basefont', 'bgsound', 'blink',  'body', 
27629         'frame',  'frameset', 'head',    'html',   'ilayer', 
27630         'iframe', 'layer',  'link',     'meta',    'object',   
27631         'script', 'style' ,'title',  'xml' // clean later..
27632 ];
27633 Roo.HtmlEditorCore.clean = [
27634     'script', 'style', 'title', 'xml'
27635 ];
27636 Roo.HtmlEditorCore.remove = [
27637     'font'
27638 ];
27639 // attributes..
27640
27641 Roo.HtmlEditorCore.ablack = [
27642     'on'
27643 ];
27644     
27645 Roo.HtmlEditorCore.aclean = [ 
27646     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27647 ];
27648
27649 // protocols..
27650 Roo.HtmlEditorCore.pwhite= [
27651         'http',  'https',  'mailto'
27652 ];
27653
27654 // white listed style attributes.
27655 Roo.HtmlEditorCore.cwhite= [
27656       //  'text-align', /// default is to allow most things..
27657       
27658          
27659 //        'font-size'//??
27660 ];
27661
27662 // black listed style attributes.
27663 Roo.HtmlEditorCore.cblack= [
27664       //  'font-size' -- this can be set by the project 
27665 ];
27666
27667
27668 Roo.HtmlEditorCore.swapCodes   =[ 
27669     [    8211, "&#8211;" ], 
27670     [    8212, "&#8212;" ], 
27671     [    8216,  "'" ],  
27672     [    8217, "'" ],  
27673     [    8220, '"' ],  
27674     [    8221, '"' ],  
27675     [    8226, "*" ],  
27676     [    8230, "..." ]
27677 ]; 
27678
27679     /*
27680  * - LGPL
27681  *
27682  * HtmlEditor
27683  * 
27684  */
27685
27686 /**
27687  * @class Roo.bootstrap.form.HtmlEditor
27688  * @extends Roo.bootstrap.form.TextArea
27689  * Bootstrap HtmlEditor class
27690
27691  * @constructor
27692  * Create a new HtmlEditor
27693  * @param {Object} config The config object
27694  */
27695
27696 Roo.bootstrap.form.HtmlEditor = function(config){
27697     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27698     if (!this.toolbars) {
27699         this.toolbars = [];
27700     }
27701     
27702     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27703     this.addEvents({
27704             /**
27705              * @event initialize
27706              * Fires when the editor is fully initialized (including the iframe)
27707              * @param {HtmlEditor} this
27708              */
27709             initialize: true,
27710             /**
27711              * @event activate
27712              * Fires when the editor is first receives the focus. Any insertion must wait
27713              * until after this event.
27714              * @param {HtmlEditor} this
27715              */
27716             activate: true,
27717              /**
27718              * @event beforesync
27719              * Fires before the textarea is updated with content from the editor iframe. Return false
27720              * to cancel the sync.
27721              * @param {HtmlEditor} this
27722              * @param {String} html
27723              */
27724             beforesync: true,
27725              /**
27726              * @event beforepush
27727              * Fires before the iframe editor is updated with content from the textarea. Return false
27728              * to cancel the push.
27729              * @param {HtmlEditor} this
27730              * @param {String} html
27731              */
27732             beforepush: true,
27733              /**
27734              * @event sync
27735              * Fires when the textarea is updated with content from the editor iframe.
27736              * @param {HtmlEditor} this
27737              * @param {String} html
27738              */
27739             sync: true,
27740              /**
27741              * @event push
27742              * Fires when the iframe editor is updated with content from the textarea.
27743              * @param {HtmlEditor} this
27744              * @param {String} html
27745              */
27746             push: true,
27747              /**
27748              * @event editmodechange
27749              * Fires when the editor switches edit modes
27750              * @param {HtmlEditor} this
27751              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27752              */
27753             editmodechange: true,
27754             /**
27755              * @event editorevent
27756              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27757              * @param {HtmlEditor} this
27758              */
27759             editorevent: true,
27760             /**
27761              * @event firstfocus
27762              * Fires when on first focus - needed by toolbars..
27763              * @param {HtmlEditor} this
27764              */
27765             firstfocus: true,
27766             /**
27767              * @event autosave
27768              * Auto save the htmlEditor value as a file into Events
27769              * @param {HtmlEditor} this
27770              */
27771             autosave: true,
27772             /**
27773              * @event savedpreview
27774              * preview the saved version of htmlEditor
27775              * @param {HtmlEditor} this
27776              */
27777             savedpreview: true
27778         });
27779 };
27780
27781
27782 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27783     
27784     
27785       /**
27786      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27787      */
27788     toolbars : false,
27789     
27790      /**
27791     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27792     */
27793     btns : [],
27794    
27795      /**
27796      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27797      *                        Roo.resizable.
27798      */
27799     resizable : false,
27800      /**
27801      * @cfg {Number} height (in pixels)
27802      */   
27803     height: 300,
27804    /**
27805      * @cfg {Number} width (in pixels)
27806      */   
27807     width: false,
27808     
27809     /**
27810      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27811      * 
27812      */
27813     stylesheets: false,
27814     
27815     // id of frame..
27816     frameId: false,
27817     
27818     // private properties
27819     validationEvent : false,
27820     deferHeight: true,
27821     initialized : false,
27822     activated : false,
27823     
27824     onFocus : Roo.emptyFn,
27825     iframePad:3,
27826     hideMode:'offsets',
27827     
27828     tbContainer : false,
27829     
27830     bodyCls : '',
27831     
27832     toolbarContainer :function() {
27833         return this.wrap.select('.x-html-editor-tb',true).first();
27834     },
27835
27836     /**
27837      * Protected method that will not generally be called directly. It
27838      * is called when the editor creates its toolbar. Override this method if you need to
27839      * add custom toolbar buttons.
27840      * @param {HtmlEditor} editor
27841      */
27842     createToolbar : function(){
27843         Roo.log('renewing');
27844         Roo.log("create toolbars");
27845         
27846         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27847         this.toolbars[0].render(this.toolbarContainer());
27848         
27849         return;
27850         
27851 //        if (!editor.toolbars || !editor.toolbars.length) {
27852 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27853 //        }
27854 //        
27855 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27856 //            editor.toolbars[i] = Roo.factory(
27857 //                    typeof(editor.toolbars[i]) == 'string' ?
27858 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27859 //                Roo.bootstrap.form.HtmlEditor);
27860 //            editor.toolbars[i].init(editor);
27861 //        }
27862     },
27863
27864      
27865     // private
27866     onRender : function(ct, position)
27867     {
27868        // Roo.log("Call onRender: " + this.xtype);
27869         var _t = this;
27870         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27871       
27872         this.wrap = this.inputEl().wrap({
27873             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27874         });
27875         
27876         this.editorcore.onRender(ct, position);
27877          
27878         if (this.resizable) {
27879             this.resizeEl = new Roo.Resizable(this.wrap, {
27880                 pinned : true,
27881                 wrap: true,
27882                 dynamic : true,
27883                 minHeight : this.height,
27884                 height: this.height,
27885                 handles : this.resizable,
27886                 width: this.width,
27887                 listeners : {
27888                     resize : function(r, w, h) {
27889                         _t.onResize(w,h); // -something
27890                     }
27891                 }
27892             });
27893             
27894         }
27895         this.createToolbar(this);
27896        
27897         
27898         if(!this.width && this.resizable){
27899             this.setSize(this.wrap.getSize());
27900         }
27901         if (this.resizeEl) {
27902             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27903             // should trigger onReize..
27904         }
27905         
27906     },
27907
27908     // private
27909     onResize : function(w, h)
27910     {
27911         Roo.log('resize: ' +w + ',' + h );
27912         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27913         var ew = false;
27914         var eh = false;
27915         
27916         if(this.inputEl() ){
27917             if(typeof w == 'number'){
27918                 var aw = w - this.wrap.getFrameWidth('lr');
27919                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27920                 ew = aw;
27921             }
27922             if(typeof h == 'number'){
27923                  var tbh = -11;  // fixme it needs to tool bar size!
27924                 for (var i =0; i < this.toolbars.length;i++) {
27925                     // fixme - ask toolbars for heights?
27926                     tbh += this.toolbars[i].el.getHeight();
27927                     //if (this.toolbars[i].footer) {
27928                     //    tbh += this.toolbars[i].footer.el.getHeight();
27929                     //}
27930                 }
27931               
27932                 
27933                 
27934                 
27935                 
27936                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27937                 ah -= 5; // knock a few pixes off for look..
27938                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27939                 var eh = ah;
27940             }
27941         }
27942         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27943         this.editorcore.onResize(ew,eh);
27944         
27945     },
27946
27947     /**
27948      * Toggles the editor between standard and source edit mode.
27949      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27950      */
27951     toggleSourceEdit : function(sourceEditMode)
27952     {
27953         this.editorcore.toggleSourceEdit(sourceEditMode);
27954         
27955         if(this.editorcore.sourceEditMode){
27956             Roo.log('editor - showing textarea');
27957             
27958 //            Roo.log('in');
27959 //            Roo.log(this.syncValue());
27960             this.syncValue();
27961             this.inputEl().removeClass(['hide', 'x-hidden']);
27962             this.inputEl().dom.removeAttribute('tabIndex');
27963             this.inputEl().focus();
27964         }else{
27965             Roo.log('editor - hiding textarea');
27966 //            Roo.log('out')
27967 //            Roo.log(this.pushValue()); 
27968             this.pushValue();
27969             
27970             this.inputEl().addClass(['hide', 'x-hidden']);
27971             this.inputEl().dom.setAttribute('tabIndex', -1);
27972             //this.deferFocus();
27973         }
27974          
27975         if(this.resizable){
27976             this.setSize(this.wrap.getSize());
27977         }
27978         
27979         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27980     },
27981  
27982     // private (for BoxComponent)
27983     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27984
27985     // private (for BoxComponent)
27986     getResizeEl : function(){
27987         return this.wrap;
27988     },
27989
27990     // private (for BoxComponent)
27991     getPositionEl : function(){
27992         return this.wrap;
27993     },
27994
27995     // private
27996     initEvents : function(){
27997         this.originalValue = this.getValue();
27998     },
27999
28000 //    /**
28001 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28002 //     * @method
28003 //     */
28004 //    markInvalid : Roo.emptyFn,
28005 //    /**
28006 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28007 //     * @method
28008 //     */
28009 //    clearInvalid : Roo.emptyFn,
28010
28011     setValue : function(v){
28012         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28013         this.editorcore.pushValue();
28014     },
28015
28016      
28017     // private
28018     deferFocus : function(){
28019         this.focus.defer(10, this);
28020     },
28021
28022     // doc'ed in Field
28023     focus : function(){
28024         this.editorcore.focus();
28025         
28026     },
28027       
28028
28029     // private
28030     onDestroy : function(){
28031         
28032         
28033         
28034         if(this.rendered){
28035             
28036             for (var i =0; i < this.toolbars.length;i++) {
28037                 // fixme - ask toolbars for heights?
28038                 this.toolbars[i].onDestroy();
28039             }
28040             
28041             this.wrap.dom.innerHTML = '';
28042             this.wrap.remove();
28043         }
28044     },
28045
28046     // private
28047     onFirstFocus : function(){
28048         //Roo.log("onFirstFocus");
28049         this.editorcore.onFirstFocus();
28050          for (var i =0; i < this.toolbars.length;i++) {
28051             this.toolbars[i].onFirstFocus();
28052         }
28053         
28054     },
28055     
28056     // private
28057     syncValue : function()
28058     {   
28059         this.editorcore.syncValue();
28060     },
28061     
28062     pushValue : function()
28063     {   
28064         this.editorcore.pushValue();
28065     }
28066      
28067     
28068     // hide stuff that is not compatible
28069     /**
28070      * @event blur
28071      * @hide
28072      */
28073     /**
28074      * @event change
28075      * @hide
28076      */
28077     /**
28078      * @event focus
28079      * @hide
28080      */
28081     /**
28082      * @event specialkey
28083      * @hide
28084      */
28085     /**
28086      * @cfg {String} fieldClass @hide
28087      */
28088     /**
28089      * @cfg {String} focusClass @hide
28090      */
28091     /**
28092      * @cfg {String} autoCreate @hide
28093      */
28094     /**
28095      * @cfg {String} inputType @hide
28096      */
28097      
28098     /**
28099      * @cfg {String} invalidText @hide
28100      */
28101     /**
28102      * @cfg {String} msgFx @hide
28103      */
28104     /**
28105      * @cfg {String} validateOnBlur @hide
28106      */
28107 });
28108  
28109     
28110    
28111    
28112    
28113       
28114 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28115 /**
28116  * @class Roo.bootstrap.form.HtmlEditorToolbar1
28117  * @extends Roo.bootstrap.nav.Simplebar
28118  * Basic Toolbar
28119  * 
28120  * @example
28121  * Usage:
28122  *
28123  new Roo.bootstrap.form.HtmlEditor({
28124     ....
28125     toolbars : [
28126         new Roo.bootstrap.form.HtmlEditorToolbar1({
28127             disable : { fonts: 1 , format: 1, ..., ... , ...],
28128             btns : [ .... ]
28129         })
28130     }
28131      
28132  * 
28133  * @cfg {Object} disable List of elements to disable..
28134  * @cfg {Array} btns List of additional buttons.
28135  * 
28136  * 
28137  * NEEDS Extra CSS? 
28138  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28139  */
28140  
28141 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28142 {
28143     
28144     Roo.apply(this, config);
28145     
28146     // default disabled, based on 'good practice'..
28147     this.disable = this.disable || {};
28148     Roo.applyIf(this.disable, {
28149         fontSize : true,
28150         colors : true,
28151         specialElements : true
28152     });
28153     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28154     
28155     this.editor = config.editor;
28156     this.editorcore = config.editor.editorcore;
28157     
28158     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28159     
28160     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28161     // dont call parent... till later.
28162 }
28163 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28164      
28165     bar : true,
28166     
28167     editor : false,
28168     editorcore : false,
28169     
28170     
28171     formats : [
28172         "p" ,  
28173         "h1","h2","h3","h4","h5","h6", 
28174         "pre", "code", 
28175         "abbr", "acronym", "address", "cite", "samp", "var",
28176         'div','span'
28177     ],
28178     
28179     onRender : function(ct, position)
28180     {
28181        // Roo.log("Call onRender: " + this.xtype);
28182         
28183        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28184        Roo.log(this.el);
28185        this.el.dom.style.marginBottom = '0';
28186        var _this = this;
28187        var editorcore = this.editorcore;
28188        var editor= this.editor;
28189        
28190        var children = [];
28191        var btn = function(id,cmd , toggle, handler, html){
28192        
28193             var  event = toggle ? 'toggle' : 'click';
28194        
28195             var a = {
28196                 size : 'sm',
28197                 xtype: 'Button',
28198                 xns: Roo.bootstrap,
28199                 //glyphicon : id,
28200                 fa: id,
28201                 cmd : id || cmd,
28202                 enableToggle:toggle !== false,
28203                 html : html || '',
28204                 pressed : toggle ? false : null,
28205                 listeners : {}
28206             };
28207             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28208                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28209             };
28210             children.push(a);
28211             return a;
28212        }
28213        
28214     //    var cb_box = function...
28215         
28216         var style = {
28217                 xtype: 'Button',
28218                 size : 'sm',
28219                 xns: Roo.bootstrap,
28220                 fa : 'font',
28221                 //html : 'submit'
28222                 menu : {
28223                     xtype: 'Menu',
28224                     xns: Roo.bootstrap,
28225                     items:  []
28226                 }
28227         };
28228         Roo.each(this.formats, function(f) {
28229             style.menu.items.push({
28230                 xtype :'MenuItem',
28231                 xns: Roo.bootstrap,
28232                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28233                 tagname : f,
28234                 listeners : {
28235                     click : function()
28236                     {
28237                         editorcore.insertTag(this.tagname);
28238                         editor.focus();
28239                     }
28240                 }
28241                 
28242             });
28243         });
28244         children.push(style);   
28245         
28246         btn('bold',false,true);
28247         btn('italic',false,true);
28248         btn('align-left', 'justifyleft',true);
28249         btn('align-center', 'justifycenter',true);
28250         btn('align-right' , 'justifyright',true);
28251         btn('link', false, false, function(btn) {
28252             //Roo.log("create link?");
28253             var url = prompt(this.createLinkText, this.defaultLinkValue);
28254             if(url && url != 'http:/'+'/'){
28255                 this.editorcore.relayCmd('createlink', url);
28256             }
28257         }),
28258         btn('list','insertunorderedlist',true);
28259         btn('pencil', false,true, function(btn){
28260                 Roo.log(this);
28261                 this.toggleSourceEdit(btn.pressed);
28262         });
28263         
28264         if (this.editor.btns.length > 0) {
28265             for (var i = 0; i<this.editor.btns.length; i++) {
28266                 children.push(this.editor.btns[i]);
28267             }
28268         }
28269         
28270         /*
28271         var cog = {
28272                 xtype: 'Button',
28273                 size : 'sm',
28274                 xns: Roo.bootstrap,
28275                 glyphicon : 'cog',
28276                 //html : 'submit'
28277                 menu : {
28278                     xtype: 'Menu',
28279                     xns: Roo.bootstrap,
28280                     items:  []
28281                 }
28282         };
28283         
28284         cog.menu.items.push({
28285             xtype :'MenuItem',
28286             xns: Roo.bootstrap,
28287             html : Clean styles,
28288             tagname : f,
28289             listeners : {
28290                 click : function()
28291                 {
28292                     editorcore.insertTag(this.tagname);
28293                     editor.focus();
28294                 }
28295             }
28296             
28297         });
28298        */
28299         
28300          
28301        this.xtype = 'NavSimplebar';
28302         
28303         for(var i=0;i< children.length;i++) {
28304             
28305             this.buttons.add(this.addxtypeChild(children[i]));
28306             
28307         }
28308         
28309         editor.on('editorevent', this.updateToolbar, this);
28310     },
28311     onBtnClick : function(id)
28312     {
28313        this.editorcore.relayCmd(id);
28314        this.editorcore.focus();
28315     },
28316     
28317     /**
28318      * Protected method that will not generally be called directly. It triggers
28319      * a toolbar update by reading the markup state of the current selection in the editor.
28320      */
28321     updateToolbar: function(){
28322
28323         if(!this.editorcore.activated){
28324             this.editor.onFirstFocus(); // is this neeed?
28325             return;
28326         }
28327
28328         var btns = this.buttons; 
28329         var doc = this.editorcore.doc;
28330         btns.get('bold').setActive(doc.queryCommandState('bold'));
28331         btns.get('italic').setActive(doc.queryCommandState('italic'));
28332         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28333         
28334         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28335         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28336         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28337         
28338         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28339         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28340          /*
28341         
28342         var ans = this.editorcore.getAllAncestors();
28343         if (this.formatCombo) {
28344             
28345             
28346             var store = this.formatCombo.store;
28347             this.formatCombo.setValue("");
28348             for (var i =0; i < ans.length;i++) {
28349                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28350                     // select it..
28351                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28352                     break;
28353                 }
28354             }
28355         }
28356         
28357         
28358         
28359         // hides menus... - so this cant be on a menu...
28360         Roo.bootstrap.MenuMgr.hideAll();
28361         */
28362         Roo.bootstrap.menu.Manager.hideAll();
28363         //this.editorsyncValue();
28364     },
28365     onFirstFocus: function() {
28366         this.buttons.each(function(item){
28367            item.enable();
28368         });
28369     },
28370     toggleSourceEdit : function(sourceEditMode){
28371         
28372           
28373         if(sourceEditMode){
28374             Roo.log("disabling buttons");
28375            this.buttons.each( function(item){
28376                 if(item.cmd != 'pencil'){
28377                     item.disable();
28378                 }
28379             });
28380           
28381         }else{
28382             Roo.log("enabling buttons");
28383             if(this.editorcore.initialized){
28384                 this.buttons.each( function(item){
28385                     item.enable();
28386                 });
28387             }
28388             
28389         }
28390         Roo.log("calling toggole on editor");
28391         // tell the editor that it's been pressed..
28392         this.editor.toggleSourceEdit(sourceEditMode);
28393        
28394     }
28395 });
28396
28397
28398
28399
28400  
28401 /*
28402  * - LGPL
28403  */
28404
28405 /**
28406  * @class Roo.bootstrap.form.Markdown
28407  * @extends Roo.bootstrap.form.TextArea
28408  * Bootstrap Showdown editable area
28409  * @cfg {string} content
28410  * 
28411  * @constructor
28412  * Create a new Showdown
28413  */
28414
28415 Roo.bootstrap.form.Markdown = function(config){
28416     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28417    
28418 };
28419
28420 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28421     
28422     editing :false,
28423     
28424     initEvents : function()
28425     {
28426         
28427         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28428         this.markdownEl = this.el.createChild({
28429             cls : 'roo-markdown-area'
28430         });
28431         this.inputEl().addClass('d-none');
28432         if (this.getValue() == '') {
28433             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28434             
28435         } else {
28436             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28437         }
28438         this.markdownEl.on('click', this.toggleTextEdit, this);
28439         this.on('blur', this.toggleTextEdit, this);
28440         this.on('specialkey', this.resizeTextArea, this);
28441     },
28442     
28443     toggleTextEdit : function()
28444     {
28445         var sh = this.markdownEl.getHeight();
28446         this.inputEl().addClass('d-none');
28447         this.markdownEl.addClass('d-none');
28448         if (!this.editing) {
28449             // show editor?
28450             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28451             this.inputEl().removeClass('d-none');
28452             this.inputEl().focus();
28453             this.editing = true;
28454             return;
28455         }
28456         // show showdown...
28457         this.updateMarkdown();
28458         this.markdownEl.removeClass('d-none');
28459         this.editing = false;
28460         return;
28461     },
28462     updateMarkdown : function()
28463     {
28464         if (this.getValue() == '') {
28465             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28466             return;
28467         }
28468  
28469         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28470     },
28471     
28472     resizeTextArea: function () {
28473         
28474         var sh = 100;
28475         Roo.log([sh, this.getValue().split("\n").length * 30]);
28476         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28477     },
28478     setValue : function(val)
28479     {
28480         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28481         if (!this.editing) {
28482             this.updateMarkdown();
28483         }
28484         
28485     },
28486     focus : function()
28487     {
28488         if (!this.editing) {
28489             this.toggleTextEdit();
28490         }
28491         
28492     }
28493
28494
28495 });/*
28496  * Based on:
28497  * Ext JS Library 1.1.1
28498  * Copyright(c) 2006-2007, Ext JS, LLC.
28499  *
28500  * Originally Released Under LGPL - original licence link has changed is not relivant.
28501  *
28502  * Fork - LGPL
28503  * <script type="text/javascript">
28504  */
28505  
28506 /**
28507  * @class Roo.bootstrap.PagingToolbar
28508  * @extends Roo.bootstrap.nav.Simplebar
28509  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28510  * @constructor
28511  * Create a new PagingToolbar
28512  * @param {Object} config The config object
28513  * @param {Roo.data.Store} store
28514  */
28515 Roo.bootstrap.PagingToolbar = function(config)
28516 {
28517     // old args format still supported... - xtype is prefered..
28518         // created from xtype...
28519     
28520     this.ds = config.dataSource;
28521     
28522     if (config.store && !this.ds) {
28523         this.store= Roo.factory(config.store, Roo.data);
28524         this.ds = this.store;
28525         this.ds.xmodule = this.xmodule || false;
28526     }
28527     
28528     this.toolbarItems = [];
28529     if (config.items) {
28530         this.toolbarItems = config.items;
28531     }
28532     
28533     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28534     
28535     this.cursor = 0;
28536     
28537     if (this.ds) { 
28538         this.bind(this.ds);
28539     }
28540     
28541     if (Roo.bootstrap.version == 4) {
28542         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28543     } else {
28544         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28545     }
28546     
28547 };
28548
28549 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28550     /**
28551      * @cfg {Roo.bootstrap.Button} buttons[]
28552      * Buttons for the toolbar
28553      */
28554      /**
28555      * @cfg {Roo.data.Store} store
28556      * The underlying data store providing the paged data
28557      */
28558     /**
28559      * @cfg {String/HTMLElement/Element} container
28560      * container The id or element that will contain the toolbar
28561      */
28562     /**
28563      * @cfg {Boolean} displayInfo
28564      * True to display the displayMsg (defaults to false)
28565      */
28566     /**
28567      * @cfg {Number} pageSize
28568      * The number of records to display per page (defaults to 20)
28569      */
28570     pageSize: 20,
28571     /**
28572      * @cfg {String} displayMsg
28573      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28574      */
28575     displayMsg : 'Displaying {0} - {1} of {2}',
28576     /**
28577      * @cfg {String} emptyMsg
28578      * The message to display when no records are found (defaults to "No data to display")
28579      */
28580     emptyMsg : 'No data to display',
28581     /**
28582      * Customizable piece of the default paging text (defaults to "Page")
28583      * @type String
28584      */
28585     beforePageText : "Page",
28586     /**
28587      * Customizable piece of the default paging text (defaults to "of %0")
28588      * @type String
28589      */
28590     afterPageText : "of {0}",
28591     /**
28592      * Customizable piece of the default paging text (defaults to "First Page")
28593      * @type String
28594      */
28595     firstText : "First Page",
28596     /**
28597      * Customizable piece of the default paging text (defaults to "Previous Page")
28598      * @type String
28599      */
28600     prevText : "Previous Page",
28601     /**
28602      * Customizable piece of the default paging text (defaults to "Next Page")
28603      * @type String
28604      */
28605     nextText : "Next Page",
28606     /**
28607      * Customizable piece of the default paging text (defaults to "Last Page")
28608      * @type String
28609      */
28610     lastText : "Last Page",
28611     /**
28612      * Customizable piece of the default paging text (defaults to "Refresh")
28613      * @type String
28614      */
28615     refreshText : "Refresh",
28616
28617     buttons : false,
28618     // private
28619     onRender : function(ct, position) 
28620     {
28621         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28622         this.navgroup.parentId = this.id;
28623         this.navgroup.onRender(this.el, null);
28624         // add the buttons to the navgroup
28625         
28626         if(this.displayInfo){
28627             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28628             this.displayEl = this.el.select('.x-paging-info', true).first();
28629 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28630 //            this.displayEl = navel.el.select('span',true).first();
28631         }
28632         
28633         var _this = this;
28634         
28635         if(this.buttons){
28636             Roo.each(_this.buttons, function(e){ // this might need to use render????
28637                Roo.factory(e).render(_this.el);
28638             });
28639         }
28640             
28641         Roo.each(_this.toolbarItems, function(e) {
28642             _this.navgroup.addItem(e);
28643         });
28644         
28645         
28646         this.first = this.navgroup.addItem({
28647             tooltip: this.firstText,
28648             cls: "prev btn-outline-secondary",
28649             html : ' <i class="fa fa-step-backward"></i>',
28650             disabled: true,
28651             preventDefault: true,
28652             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28653         });
28654         
28655         this.prev =  this.navgroup.addItem({
28656             tooltip: this.prevText,
28657             cls: "prev btn-outline-secondary",
28658             html : ' <i class="fa fa-backward"></i>',
28659             disabled: true,
28660             preventDefault: true,
28661             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28662         });
28663     //this.addSeparator();
28664         
28665         
28666         var field = this.navgroup.addItem( {
28667             tagtype : 'span',
28668             cls : 'x-paging-position  btn-outline-secondary',
28669              disabled: true,
28670             html : this.beforePageText  +
28671                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28672                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28673          } ); //?? escaped?
28674         
28675         this.field = field.el.select('input', true).first();
28676         this.field.on("keydown", this.onPagingKeydown, this);
28677         this.field.on("focus", function(){this.dom.select();});
28678     
28679     
28680         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28681         //this.field.setHeight(18);
28682         //this.addSeparator();
28683         this.next = this.navgroup.addItem({
28684             tooltip: this.nextText,
28685             cls: "next btn-outline-secondary",
28686             html : ' <i class="fa fa-forward"></i>',
28687             disabled: true,
28688             preventDefault: true,
28689             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28690         });
28691         this.last = this.navgroup.addItem({
28692             tooltip: this.lastText,
28693             html : ' <i class="fa fa-step-forward"></i>',
28694             cls: "next btn-outline-secondary",
28695             disabled: true,
28696             preventDefault: true,
28697             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28698         });
28699     //this.addSeparator();
28700         this.loading = this.navgroup.addItem({
28701             tooltip: this.refreshText,
28702             cls: "btn-outline-secondary",
28703             html : ' <i class="fa fa-refresh"></i>',
28704             preventDefault: true,
28705             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28706         });
28707         
28708     },
28709
28710     // private
28711     updateInfo : function(){
28712         if(this.displayEl){
28713             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28714             var msg = count == 0 ?
28715                 this.emptyMsg :
28716                 String.format(
28717                     this.displayMsg,
28718                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28719                 );
28720             this.displayEl.update(msg);
28721         }
28722     },
28723
28724     // private
28725     onLoad : function(ds, r, o)
28726     {
28727         this.cursor = o.params && o.params.start ? o.params.start : 0;
28728         
28729         var d = this.getPageData(),
28730             ap = d.activePage,
28731             ps = d.pages;
28732         
28733         
28734         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28735         this.field.dom.value = ap;
28736         this.first.setDisabled(ap == 1);
28737         this.prev.setDisabled(ap == 1);
28738         this.next.setDisabled(ap == ps);
28739         this.last.setDisabled(ap == ps);
28740         this.loading.enable();
28741         this.updateInfo();
28742     },
28743
28744     // private
28745     getPageData : function(){
28746         var total = this.ds.getTotalCount();
28747         return {
28748             total : total,
28749             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28750             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28751         };
28752     },
28753
28754     // private
28755     onLoadError : function(){
28756         this.loading.enable();
28757     },
28758
28759     // private
28760     onPagingKeydown : function(e){
28761         var k = e.getKey();
28762         var d = this.getPageData();
28763         if(k == e.RETURN){
28764             var v = this.field.dom.value, pageNum;
28765             if(!v || isNaN(pageNum = parseInt(v, 10))){
28766                 this.field.dom.value = d.activePage;
28767                 return;
28768             }
28769             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28770             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28771             e.stopEvent();
28772         }
28773         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))
28774         {
28775           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28776           this.field.dom.value = pageNum;
28777           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28778           e.stopEvent();
28779         }
28780         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28781         {
28782           var v = this.field.dom.value, pageNum; 
28783           var increment = (e.shiftKey) ? 10 : 1;
28784           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28785                 increment *= -1;
28786           }
28787           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28788             this.field.dom.value = d.activePage;
28789             return;
28790           }
28791           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28792           {
28793             this.field.dom.value = parseInt(v, 10) + increment;
28794             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28795             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28796           }
28797           e.stopEvent();
28798         }
28799     },
28800
28801     // private
28802     beforeLoad : function(){
28803         if(this.loading){
28804             this.loading.disable();
28805         }
28806     },
28807
28808     // private
28809     onClick : function(which){
28810         
28811         var ds = this.ds;
28812         if (!ds) {
28813             return;
28814         }
28815         
28816         switch(which){
28817             case "first":
28818                 ds.load({params:{start: 0, limit: this.pageSize}});
28819             break;
28820             case "prev":
28821                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28822             break;
28823             case "next":
28824                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28825             break;
28826             case "last":
28827                 var total = ds.getTotalCount();
28828                 var extra = total % this.pageSize;
28829                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28830                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28831             break;
28832             case "refresh":
28833                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28834             break;
28835         }
28836     },
28837
28838     /**
28839      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28840      * @param {Roo.data.Store} store The data store to unbind
28841      */
28842     unbind : function(ds){
28843         ds.un("beforeload", this.beforeLoad, this);
28844         ds.un("load", this.onLoad, this);
28845         ds.un("loadexception", this.onLoadError, this);
28846         ds.un("remove", this.updateInfo, this);
28847         ds.un("add", this.updateInfo, this);
28848         this.ds = undefined;
28849     },
28850
28851     /**
28852      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28853      * @param {Roo.data.Store} store The data store to bind
28854      */
28855     bind : function(ds){
28856         ds.on("beforeload", this.beforeLoad, this);
28857         ds.on("load", this.onLoad, this);
28858         ds.on("loadexception", this.onLoadError, this);
28859         ds.on("remove", this.updateInfo, this);
28860         ds.on("add", this.updateInfo, this);
28861         this.ds = ds;
28862     }
28863 });/*
28864  * - LGPL
28865  *
28866  * element
28867  * 
28868  */
28869
28870 /**
28871  * @class Roo.bootstrap.MessageBar
28872  * @extends Roo.bootstrap.Component
28873  * Bootstrap MessageBar class
28874  * @cfg {String} html contents of the MessageBar
28875  * @cfg {String} weight (info | success | warning | danger) default info
28876  * @cfg {String} beforeClass insert the bar before the given class
28877  * @cfg {Boolean} closable (true | false) default false
28878  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28879  * 
28880  * @constructor
28881  * Create a new Element
28882  * @param {Object} config The config object
28883  */
28884
28885 Roo.bootstrap.MessageBar = function(config){
28886     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28887 };
28888
28889 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28890     
28891     html: '',
28892     weight: 'info',
28893     closable: false,
28894     fixed: false,
28895     beforeClass: 'bootstrap-sticky-wrap',
28896     
28897     getAutoCreate : function(){
28898         
28899         var cfg = {
28900             tag: 'div',
28901             cls: 'alert alert-dismissable alert-' + this.weight,
28902             cn: [
28903                 {
28904                     tag: 'span',
28905                     cls: 'message',
28906                     html: this.html || ''
28907                 }
28908             ]
28909         };
28910         
28911         if(this.fixed){
28912             cfg.cls += ' alert-messages-fixed';
28913         }
28914         
28915         if(this.closable){
28916             cfg.cn.push({
28917                 tag: 'button',
28918                 cls: 'close',
28919                 html: 'x'
28920             });
28921         }
28922         
28923         return cfg;
28924     },
28925     
28926     onRender : function(ct, position)
28927     {
28928         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28929         
28930         if(!this.el){
28931             var cfg = Roo.apply({},  this.getAutoCreate());
28932             cfg.id = Roo.id();
28933             
28934             if (this.cls) {
28935                 cfg.cls += ' ' + this.cls;
28936             }
28937             if (this.style) {
28938                 cfg.style = this.style;
28939             }
28940             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28941             
28942             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28943         }
28944         
28945         this.el.select('>button.close').on('click', this.hide, this);
28946         
28947     },
28948     
28949     show : function()
28950     {
28951         if (!this.rendered) {
28952             this.render();
28953         }
28954         
28955         this.el.show();
28956         
28957         this.fireEvent('show', this);
28958         
28959     },
28960     
28961     hide : function()
28962     {
28963         if (!this.rendered) {
28964             this.render();
28965         }
28966         
28967         this.el.hide();
28968         
28969         this.fireEvent('hide', this);
28970     },
28971     
28972     update : function()
28973     {
28974 //        var e = this.el.dom.firstChild;
28975 //        
28976 //        if(this.closable){
28977 //            e = e.nextSibling;
28978 //        }
28979 //        
28980 //        e.data = this.html || '';
28981
28982         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28983     }
28984    
28985 });
28986
28987  
28988
28989      /*
28990  * - LGPL
28991  *
28992  * Graph
28993  * 
28994  */
28995
28996
28997 /**
28998  * @class Roo.bootstrap.Graph
28999  * @extends Roo.bootstrap.Component
29000  * Bootstrap Graph class
29001 > Prameters
29002  -sm {number} sm 4
29003  -md {number} md 5
29004  @cfg {String} graphtype  bar | vbar | pie
29005  @cfg {number} g_x coodinator | centre x (pie)
29006  @cfg {number} g_y coodinator | centre y (pie)
29007  @cfg {number} g_r radius (pie)
29008  @cfg {number} g_height height of the chart (respected by all elements in the set)
29009  @cfg {number} g_width width of the chart (respected by all elements in the set)
29010  @cfg {Object} title The title of the chart
29011     
29012  -{Array}  values
29013  -opts (object) options for the chart 
29014      o {
29015      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29016      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29017      o vgutter (number)
29018      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.
29019      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29020      o to
29021      o stretch (boolean)
29022      o }
29023  -opts (object) options for the pie
29024      o{
29025      o cut
29026      o startAngle (number)
29027      o endAngle (number)
29028      } 
29029  *
29030  * @constructor
29031  * Create a new Input
29032  * @param {Object} config The config object
29033  */
29034
29035 Roo.bootstrap.Graph = function(config){
29036     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29037     
29038     this.addEvents({
29039         // img events
29040         /**
29041          * @event click
29042          * The img click event for the img.
29043          * @param {Roo.EventObject} e
29044          */
29045         "click" : true
29046     });
29047 };
29048
29049 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29050     
29051     sm: 4,
29052     md: 5,
29053     graphtype: 'bar',
29054     g_height: 250,
29055     g_width: 400,
29056     g_x: 50,
29057     g_y: 50,
29058     g_r: 30,
29059     opts:{
29060         //g_colors: this.colors,
29061         g_type: 'soft',
29062         g_gutter: '20%'
29063
29064     },
29065     title : false,
29066
29067     getAutoCreate : function(){
29068         
29069         var cfg = {
29070             tag: 'div',
29071             html : null
29072         };
29073         
29074         
29075         return  cfg;
29076     },
29077
29078     onRender : function(ct,position){
29079         
29080         
29081         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29082         
29083         if (typeof(Raphael) == 'undefined') {
29084             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29085             return;
29086         }
29087         
29088         this.raphael = Raphael(this.el.dom);
29089         
29090                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29091                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29092                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29093                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29094                 /*
29095                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29096                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29097                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29098                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29099                 
29100                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29101                 r.barchart(330, 10, 300, 220, data1);
29102                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29103                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29104                 */
29105                 
29106                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29107                 // r.barchart(30, 30, 560, 250,  xdata, {
29108                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29109                 //     axis : "0 0 1 1",
29110                 //     axisxlabels :  xdata
29111                 //     //yvalues : cols,
29112                    
29113                 // });
29114 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29115 //        
29116 //        this.load(null,xdata,{
29117 //                axis : "0 0 1 1",
29118 //                axisxlabels :  xdata
29119 //                });
29120
29121     },
29122
29123     load : function(graphtype,xdata,opts)
29124     {
29125         this.raphael.clear();
29126         if(!graphtype) {
29127             graphtype = this.graphtype;
29128         }
29129         if(!opts){
29130             opts = this.opts;
29131         }
29132         var r = this.raphael,
29133             fin = function () {
29134                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29135             },
29136             fout = function () {
29137                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29138             },
29139             pfin = function() {
29140                 this.sector.stop();
29141                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29142
29143                 if (this.label) {
29144                     this.label[0].stop();
29145                     this.label[0].attr({ r: 7.5 });
29146                     this.label[1].attr({ "font-weight": 800 });
29147                 }
29148             },
29149             pfout = function() {
29150                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29151
29152                 if (this.label) {
29153                     this.label[0].animate({ r: 5 }, 500, "bounce");
29154                     this.label[1].attr({ "font-weight": 400 });
29155                 }
29156             };
29157
29158         switch(graphtype){
29159             case 'bar':
29160                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29161                 break;
29162             case 'hbar':
29163                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29164                 break;
29165             case 'pie':
29166 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29167 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29168 //            
29169                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29170                 
29171                 break;
29172
29173         }
29174         
29175         if(this.title){
29176             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29177         }
29178         
29179     },
29180     
29181     setTitle: function(o)
29182     {
29183         this.title = o;
29184     },
29185     
29186     initEvents: function() {
29187         
29188         if(!this.href){
29189             this.el.on('click', this.onClick, this);
29190         }
29191     },
29192     
29193     onClick : function(e)
29194     {
29195         Roo.log('img onclick');
29196         this.fireEvent('click', this, e);
29197     }
29198    
29199 });
29200
29201  
29202 /*
29203  * - LGPL
29204  *
29205  * numberBox
29206  * 
29207  */
29208 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29209
29210 /**
29211  * @class Roo.bootstrap.dash.NumberBox
29212  * @extends Roo.bootstrap.Component
29213  * Bootstrap NumberBox class
29214  * @cfg {String} headline Box headline
29215  * @cfg {String} content Box content
29216  * @cfg {String} icon Box icon
29217  * @cfg {String} footer Footer text
29218  * @cfg {String} fhref Footer href
29219  * 
29220  * @constructor
29221  * Create a new NumberBox
29222  * @param {Object} config The config object
29223  */
29224
29225
29226 Roo.bootstrap.dash.NumberBox = function(config){
29227     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29228     
29229 };
29230
29231 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29232     
29233     headline : '',
29234     content : '',
29235     icon : '',
29236     footer : '',
29237     fhref : '',
29238     ficon : '',
29239     
29240     getAutoCreate : function(){
29241         
29242         var cfg = {
29243             tag : 'div',
29244             cls : 'small-box ',
29245             cn : [
29246                 {
29247                     tag : 'div',
29248                     cls : 'inner',
29249                     cn :[
29250                         {
29251                             tag : 'h3',
29252                             cls : 'roo-headline',
29253                             html : this.headline
29254                         },
29255                         {
29256                             tag : 'p',
29257                             cls : 'roo-content',
29258                             html : this.content
29259                         }
29260                     ]
29261                 }
29262             ]
29263         };
29264         
29265         if(this.icon){
29266             cfg.cn.push({
29267                 tag : 'div',
29268                 cls : 'icon',
29269                 cn :[
29270                     {
29271                         tag : 'i',
29272                         cls : 'ion ' + this.icon
29273                     }
29274                 ]
29275             });
29276         }
29277         
29278         if(this.footer){
29279             var footer = {
29280                 tag : 'a',
29281                 cls : 'small-box-footer',
29282                 href : this.fhref || '#',
29283                 html : this.footer
29284             };
29285             
29286             cfg.cn.push(footer);
29287             
29288         }
29289         
29290         return  cfg;
29291     },
29292
29293     onRender : function(ct,position){
29294         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29295
29296
29297        
29298                 
29299     },
29300
29301     setHeadline: function (value)
29302     {
29303         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29304     },
29305     
29306     setFooter: function (value, href)
29307     {
29308         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29309         
29310         if(href){
29311             this.el.select('a.small-box-footer',true).first().attr('href', href);
29312         }
29313         
29314     },
29315
29316     setContent: function (value)
29317     {
29318         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29319     },
29320
29321     initEvents: function() 
29322     {   
29323         
29324     }
29325     
29326 });
29327
29328  
29329 /*
29330  * - LGPL
29331  *
29332  * TabBox
29333  * 
29334  */
29335 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29336
29337 /**
29338  * @class Roo.bootstrap.dash.TabBox
29339  * @extends Roo.bootstrap.Component
29340  * @children Roo.bootstrap.dash.TabPane
29341  * Bootstrap TabBox class
29342  * @cfg {String} title Title of the TabBox
29343  * @cfg {String} icon Icon of the TabBox
29344  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29345  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29346  * 
29347  * @constructor
29348  * Create a new TabBox
29349  * @param {Object} config The config object
29350  */
29351
29352
29353 Roo.bootstrap.dash.TabBox = function(config){
29354     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29355     this.addEvents({
29356         // raw events
29357         /**
29358          * @event addpane
29359          * When a pane is added
29360          * @param {Roo.bootstrap.dash.TabPane} pane
29361          */
29362         "addpane" : true,
29363         /**
29364          * @event activatepane
29365          * When a pane is activated
29366          * @param {Roo.bootstrap.dash.TabPane} pane
29367          */
29368         "activatepane" : true
29369         
29370          
29371     });
29372     
29373     this.panes = [];
29374 };
29375
29376 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29377
29378     title : '',
29379     icon : false,
29380     showtabs : true,
29381     tabScrollable : false,
29382     
29383     getChildContainer : function()
29384     {
29385         return this.el.select('.tab-content', true).first();
29386     },
29387     
29388     getAutoCreate : function(){
29389         
29390         var header = {
29391             tag: 'li',
29392             cls: 'pull-left header',
29393             html: this.title,
29394             cn : []
29395         };
29396         
29397         if(this.icon){
29398             header.cn.push({
29399                 tag: 'i',
29400                 cls: 'fa ' + this.icon
29401             });
29402         }
29403         
29404         var h = {
29405             tag: 'ul',
29406             cls: 'nav nav-tabs pull-right',
29407             cn: [
29408                 header
29409             ]
29410         };
29411         
29412         if(this.tabScrollable){
29413             h = {
29414                 tag: 'div',
29415                 cls: 'tab-header',
29416                 cn: [
29417                     {
29418                         tag: 'ul',
29419                         cls: 'nav nav-tabs pull-right',
29420                         cn: [
29421                             header
29422                         ]
29423                     }
29424                 ]
29425             };
29426         }
29427         
29428         var cfg = {
29429             tag: 'div',
29430             cls: 'nav-tabs-custom',
29431             cn: [
29432                 h,
29433                 {
29434                     tag: 'div',
29435                     cls: 'tab-content no-padding',
29436                     cn: []
29437                 }
29438             ]
29439         };
29440
29441         return  cfg;
29442     },
29443     initEvents : function()
29444     {
29445         //Roo.log('add add pane handler');
29446         this.on('addpane', this.onAddPane, this);
29447     },
29448      /**
29449      * Updates the box title
29450      * @param {String} html to set the title to.
29451      */
29452     setTitle : function(value)
29453     {
29454         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29455     },
29456     onAddPane : function(pane)
29457     {
29458         this.panes.push(pane);
29459         //Roo.log('addpane');
29460         //Roo.log(pane);
29461         // tabs are rendere left to right..
29462         if(!this.showtabs){
29463             return;
29464         }
29465         
29466         var ctr = this.el.select('.nav-tabs', true).first();
29467          
29468          
29469         var existing = ctr.select('.nav-tab',true);
29470         var qty = existing.getCount();;
29471         
29472         
29473         var tab = ctr.createChild({
29474             tag : 'li',
29475             cls : 'nav-tab' + (qty ? '' : ' active'),
29476             cn : [
29477                 {
29478                     tag : 'a',
29479                     href:'#',
29480                     html : pane.title
29481                 }
29482             ]
29483         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29484         pane.tab = tab;
29485         
29486         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29487         if (!qty) {
29488             pane.el.addClass('active');
29489         }
29490         
29491                 
29492     },
29493     onTabClick : function(ev,un,ob,pane)
29494     {
29495         //Roo.log('tab - prev default');
29496         ev.preventDefault();
29497         
29498         
29499         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29500         pane.tab.addClass('active');
29501         //Roo.log(pane.title);
29502         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29503         // technically we should have a deactivate event.. but maybe add later.
29504         // and it should not de-activate the selected tab...
29505         this.fireEvent('activatepane', pane);
29506         pane.el.addClass('active');
29507         pane.fireEvent('activate');
29508         
29509         
29510     },
29511     
29512     getActivePane : function()
29513     {
29514         var r = false;
29515         Roo.each(this.panes, function(p) {
29516             if(p.el.hasClass('active')){
29517                 r = p;
29518                 return false;
29519             }
29520             
29521             return;
29522         });
29523         
29524         return r;
29525     }
29526     
29527     
29528 });
29529
29530  
29531 /*
29532  * - LGPL
29533  *
29534  * Tab pane
29535  * 
29536  */
29537 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29538 /**
29539  * @class Roo.bootstrap.TabPane
29540  * @extends Roo.bootstrap.Component
29541  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29542  * Bootstrap TabPane class
29543  * @cfg {Boolean} active (false | true) Default false
29544  * @cfg {String} title title of panel
29545
29546  * 
29547  * @constructor
29548  * Create a new TabPane
29549  * @param {Object} config The config object
29550  */
29551
29552 Roo.bootstrap.dash.TabPane = function(config){
29553     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29554     
29555     this.addEvents({
29556         // raw events
29557         /**
29558          * @event activate
29559          * When a pane is activated
29560          * @param {Roo.bootstrap.dash.TabPane} pane
29561          */
29562         "activate" : true
29563          
29564     });
29565 };
29566
29567 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29568     
29569     active : false,
29570     title : '',
29571     
29572     // the tabBox that this is attached to.
29573     tab : false,
29574      
29575     getAutoCreate : function() 
29576     {
29577         var cfg = {
29578             tag: 'div',
29579             cls: 'tab-pane'
29580         };
29581         
29582         if(this.active){
29583             cfg.cls += ' active';
29584         }
29585         
29586         return cfg;
29587     },
29588     initEvents  : function()
29589     {
29590         //Roo.log('trigger add pane handler');
29591         this.parent().fireEvent('addpane', this)
29592     },
29593     
29594      /**
29595      * Updates the tab title 
29596      * @param {String} html to set the title to.
29597      */
29598     setTitle: function(str)
29599     {
29600         if (!this.tab) {
29601             return;
29602         }
29603         this.title = str;
29604         this.tab.select('a', true).first().dom.innerHTML = str;
29605         
29606     }
29607     
29608     
29609     
29610 });
29611
29612  
29613
29614
29615  /*
29616  * - LGPL
29617  *
29618  * Tooltip
29619  * 
29620  */
29621
29622 /**
29623  * @class Roo.bootstrap.Tooltip
29624  * Bootstrap Tooltip class
29625  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29626  * to determine which dom element triggers the tooltip.
29627  * 
29628  * It needs to add support for additional attributes like tooltip-position
29629  * 
29630  * @constructor
29631  * Create a new Toolti
29632  * @param {Object} config The config object
29633  */
29634
29635 Roo.bootstrap.Tooltip = function(config){
29636     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29637     
29638     this.alignment = Roo.bootstrap.Tooltip.alignment;
29639     
29640     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29641         this.alignment = config.alignment;
29642     }
29643     
29644 };
29645
29646 Roo.apply(Roo.bootstrap.Tooltip, {
29647     /**
29648      * @function init initialize tooltip monitoring.
29649      * @static
29650      */
29651     currentEl : false,
29652     currentTip : false,
29653     currentRegion : false,
29654     
29655     //  init : delay?
29656     
29657     init : function()
29658     {
29659         Roo.get(document).on('mouseover', this.enter ,this);
29660         Roo.get(document).on('mouseout', this.leave, this);
29661          
29662         
29663         this.currentTip = new Roo.bootstrap.Tooltip();
29664     },
29665     
29666     enter : function(ev)
29667     {
29668         var dom = ev.getTarget();
29669         
29670         //Roo.log(['enter',dom]);
29671         var el = Roo.fly(dom);
29672         if (this.currentEl) {
29673             //Roo.log(dom);
29674             //Roo.log(this.currentEl);
29675             //Roo.log(this.currentEl.contains(dom));
29676             if (this.currentEl == el) {
29677                 return;
29678             }
29679             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29680                 return;
29681             }
29682
29683         }
29684         
29685         if (this.currentTip.el) {
29686             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29687         }    
29688         //Roo.log(ev);
29689         
29690         if(!el || el.dom == document){
29691             return;
29692         }
29693         
29694         var bindEl = el; 
29695         var pel = false;
29696         if (!el.attr('tooltip')) {
29697             pel = el.findParent("[tooltip]");
29698             if (pel) {
29699                 bindEl = Roo.get(pel);
29700             }
29701         }
29702         
29703        
29704         
29705         // you can not look for children, as if el is the body.. then everythign is the child..
29706         if (!pel && !el.attr('tooltip')) { //
29707             if (!el.select("[tooltip]").elements.length) {
29708                 return;
29709             }
29710             // is the mouse over this child...?
29711             bindEl = el.select("[tooltip]").first();
29712             var xy = ev.getXY();
29713             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29714                 //Roo.log("not in region.");
29715                 return;
29716             }
29717             //Roo.log("child element over..");
29718             
29719         }
29720         this.currentEl = el;
29721         this.currentTip.bind(bindEl);
29722         this.currentRegion = Roo.lib.Region.getRegion(dom);
29723         this.currentTip.enter();
29724         
29725     },
29726     leave : function(ev)
29727     {
29728         var dom = ev.getTarget();
29729         //Roo.log(['leave',dom]);
29730         if (!this.currentEl) {
29731             return;
29732         }
29733         
29734         
29735         if (dom != this.currentEl.dom) {
29736             return;
29737         }
29738         var xy = ev.getXY();
29739         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29740             return;
29741         }
29742         // only activate leave if mouse cursor is outside... bounding box..
29743         
29744         
29745         
29746         
29747         if (this.currentTip) {
29748             this.currentTip.leave();
29749         }
29750         //Roo.log('clear currentEl');
29751         this.currentEl = false;
29752         
29753         
29754     },
29755     alignment : {
29756         'left' : ['r-l', [-2,0], 'right'],
29757         'right' : ['l-r', [2,0], 'left'],
29758         'bottom' : ['t-b', [0,2], 'top'],
29759         'top' : [ 'b-t', [0,-2], 'bottom']
29760     }
29761     
29762 });
29763
29764
29765 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29766     
29767     
29768     bindEl : false,
29769     
29770     delay : null, // can be { show : 300 , hide: 500}
29771     
29772     timeout : null,
29773     
29774     hoverState : null, //???
29775     
29776     placement : 'bottom', 
29777     
29778     alignment : false,
29779     
29780     getAutoCreate : function(){
29781     
29782         var cfg = {
29783            cls : 'tooltip',   
29784            role : 'tooltip',
29785            cn : [
29786                 {
29787                     cls : 'tooltip-arrow arrow'
29788                 },
29789                 {
29790                     cls : 'tooltip-inner'
29791                 }
29792            ]
29793         };
29794         
29795         return cfg;
29796     },
29797     bind : function(el)
29798     {
29799         this.bindEl = el;
29800     },
29801     
29802     initEvents : function()
29803     {
29804         this.arrowEl = this.el.select('.arrow', true).first();
29805         this.innerEl = this.el.select('.tooltip-inner', true).first();
29806     },
29807     
29808     enter : function () {
29809        
29810         if (this.timeout != null) {
29811             clearTimeout(this.timeout);
29812         }
29813         
29814         this.hoverState = 'in';
29815          //Roo.log("enter - show");
29816         if (!this.delay || !this.delay.show) {
29817             this.show();
29818             return;
29819         }
29820         var _t = this;
29821         this.timeout = setTimeout(function () {
29822             if (_t.hoverState == 'in') {
29823                 _t.show();
29824             }
29825         }, this.delay.show);
29826     },
29827     leave : function()
29828     {
29829         clearTimeout(this.timeout);
29830     
29831         this.hoverState = 'out';
29832          if (!this.delay || !this.delay.hide) {
29833             this.hide();
29834             return;
29835         }
29836        
29837         var _t = this;
29838         this.timeout = setTimeout(function () {
29839             //Roo.log("leave - timeout");
29840             
29841             if (_t.hoverState == 'out') {
29842                 _t.hide();
29843                 Roo.bootstrap.Tooltip.currentEl = false;
29844             }
29845         }, delay);
29846     },
29847     
29848     show : function (msg)
29849     {
29850         if (!this.el) {
29851             this.render(document.body);
29852         }
29853         // set content.
29854         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29855         
29856         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29857         
29858         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29859         
29860         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29861                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29862         
29863         var placement = typeof this.placement == 'function' ?
29864             this.placement.call(this, this.el, on_el) :
29865             this.placement;
29866             
29867         var autoToken = /\s?auto?\s?/i;
29868         var autoPlace = autoToken.test(placement);
29869         if (autoPlace) {
29870             placement = placement.replace(autoToken, '') || 'top';
29871         }
29872         
29873         //this.el.detach()
29874         //this.el.setXY([0,0]);
29875         this.el.show();
29876         //this.el.dom.style.display='block';
29877         
29878         //this.el.appendTo(on_el);
29879         
29880         var p = this.getPosition();
29881         var box = this.el.getBox();
29882         
29883         if (autoPlace) {
29884             // fixme..
29885         }
29886         
29887         var align = this.alignment[placement];
29888         
29889         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29890         
29891         if(placement == 'top' || placement == 'bottom'){
29892             if(xy[0] < 0){
29893                 placement = 'right';
29894             }
29895             
29896             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29897                 placement = 'left';
29898             }
29899             
29900             var scroll = Roo.select('body', true).first().getScroll();
29901             
29902             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29903                 placement = 'top';
29904             }
29905             
29906             align = this.alignment[placement];
29907             
29908             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29909             
29910         }
29911         
29912         var elems = document.getElementsByTagName('div');
29913         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29914         for (var i = 0; i < elems.length; i++) {
29915           var zindex = Number.parseInt(
29916                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29917                 10
29918           );
29919           if (zindex > highest) {
29920             highest = zindex;
29921           }
29922         }
29923         
29924         
29925         
29926         this.el.dom.style.zIndex = highest;
29927         
29928         this.el.alignTo(this.bindEl, align[0],align[1]);
29929         //var arrow = this.el.select('.arrow',true).first();
29930         //arrow.set(align[2], 
29931         
29932         this.el.addClass(placement);
29933         this.el.addClass("bs-tooltip-"+ placement);
29934         
29935         this.el.addClass('in fade show');
29936         
29937         this.hoverState = null;
29938         
29939         if (this.el.hasClass('fade')) {
29940             // fade it?
29941         }
29942         
29943         
29944         
29945         
29946         
29947     },
29948     hide : function()
29949     {
29950          
29951         if (!this.el) {
29952             return;
29953         }
29954         //this.el.setXY([0,0]);
29955         this.el.removeClass(['show', 'in']);
29956         //this.el.hide();
29957         
29958     }
29959     
29960 });
29961  
29962
29963  /*
29964  * - LGPL
29965  *
29966  * Location Picker
29967  * 
29968  */
29969
29970 /**
29971  * @class Roo.bootstrap.LocationPicker
29972  * @extends Roo.bootstrap.Component
29973  * Bootstrap LocationPicker class
29974  * @cfg {Number} latitude Position when init default 0
29975  * @cfg {Number} longitude Position when init default 0
29976  * @cfg {Number} zoom default 15
29977  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29978  * @cfg {Boolean} mapTypeControl default false
29979  * @cfg {Boolean} disableDoubleClickZoom default false
29980  * @cfg {Boolean} scrollwheel default true
29981  * @cfg {Boolean} streetViewControl default false
29982  * @cfg {Number} radius default 0
29983  * @cfg {String} locationName
29984  * @cfg {Boolean} draggable default true
29985  * @cfg {Boolean} enableAutocomplete default false
29986  * @cfg {Boolean} enableReverseGeocode default true
29987  * @cfg {String} markerTitle
29988  * 
29989  * @constructor
29990  * Create a new LocationPicker
29991  * @param {Object} config The config object
29992  */
29993
29994
29995 Roo.bootstrap.LocationPicker = function(config){
29996     
29997     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29998     
29999     this.addEvents({
30000         /**
30001          * @event initial
30002          * Fires when the picker initialized.
30003          * @param {Roo.bootstrap.LocationPicker} this
30004          * @param {Google Location} location
30005          */
30006         initial : true,
30007         /**
30008          * @event positionchanged
30009          * Fires when the picker position changed.
30010          * @param {Roo.bootstrap.LocationPicker} this
30011          * @param {Google Location} location
30012          */
30013         positionchanged : true,
30014         /**
30015          * @event resize
30016          * Fires when the map resize.
30017          * @param {Roo.bootstrap.LocationPicker} this
30018          */
30019         resize : true,
30020         /**
30021          * @event show
30022          * Fires when the map show.
30023          * @param {Roo.bootstrap.LocationPicker} this
30024          */
30025         show : true,
30026         /**
30027          * @event hide
30028          * Fires when the map hide.
30029          * @param {Roo.bootstrap.LocationPicker} this
30030          */
30031         hide : true,
30032         /**
30033          * @event mapClick
30034          * Fires when click the map.
30035          * @param {Roo.bootstrap.LocationPicker} this
30036          * @param {Map event} e
30037          */
30038         mapClick : true,
30039         /**
30040          * @event mapRightClick
30041          * Fires when right click the map.
30042          * @param {Roo.bootstrap.LocationPicker} this
30043          * @param {Map event} e
30044          */
30045         mapRightClick : true,
30046         /**
30047          * @event markerClick
30048          * Fires when click the marker.
30049          * @param {Roo.bootstrap.LocationPicker} this
30050          * @param {Map event} e
30051          */
30052         markerClick : true,
30053         /**
30054          * @event markerRightClick
30055          * Fires when right click the marker.
30056          * @param {Roo.bootstrap.LocationPicker} this
30057          * @param {Map event} e
30058          */
30059         markerRightClick : true,
30060         /**
30061          * @event OverlayViewDraw
30062          * Fires when OverlayView Draw
30063          * @param {Roo.bootstrap.LocationPicker} this
30064          */
30065         OverlayViewDraw : true,
30066         /**
30067          * @event OverlayViewOnAdd
30068          * Fires when OverlayView Draw
30069          * @param {Roo.bootstrap.LocationPicker} this
30070          */
30071         OverlayViewOnAdd : true,
30072         /**
30073          * @event OverlayViewOnRemove
30074          * Fires when OverlayView Draw
30075          * @param {Roo.bootstrap.LocationPicker} this
30076          */
30077         OverlayViewOnRemove : true,
30078         /**
30079          * @event OverlayViewShow
30080          * Fires when OverlayView Draw
30081          * @param {Roo.bootstrap.LocationPicker} this
30082          * @param {Pixel} cpx
30083          */
30084         OverlayViewShow : true,
30085         /**
30086          * @event OverlayViewHide
30087          * Fires when OverlayView Draw
30088          * @param {Roo.bootstrap.LocationPicker} this
30089          */
30090         OverlayViewHide : true,
30091         /**
30092          * @event loadexception
30093          * Fires when load google lib failed.
30094          * @param {Roo.bootstrap.LocationPicker} this
30095          */
30096         loadexception : true
30097     });
30098         
30099 };
30100
30101 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30102     
30103     gMapContext: false,
30104     
30105     latitude: 0,
30106     longitude: 0,
30107     zoom: 15,
30108     mapTypeId: false,
30109     mapTypeControl: false,
30110     disableDoubleClickZoom: false,
30111     scrollwheel: true,
30112     streetViewControl: false,
30113     radius: 0,
30114     locationName: '',
30115     draggable: true,
30116     enableAutocomplete: false,
30117     enableReverseGeocode: true,
30118     markerTitle: '',
30119     
30120     getAutoCreate: function()
30121     {
30122
30123         var cfg = {
30124             tag: 'div',
30125             cls: 'roo-location-picker'
30126         };
30127         
30128         return cfg
30129     },
30130     
30131     initEvents: function(ct, position)
30132     {       
30133         if(!this.el.getWidth() || this.isApplied()){
30134             return;
30135         }
30136         
30137         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30138         
30139         this.initial();
30140     },
30141     
30142     initial: function()
30143     {
30144         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30145             this.fireEvent('loadexception', this);
30146             return;
30147         }
30148         
30149         if(!this.mapTypeId){
30150             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30151         }
30152         
30153         this.gMapContext = this.GMapContext();
30154         
30155         this.initOverlayView();
30156         
30157         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30158         
30159         var _this = this;
30160                 
30161         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30162             _this.setPosition(_this.gMapContext.marker.position);
30163         });
30164         
30165         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30166             _this.fireEvent('mapClick', this, event);
30167             
30168         });
30169
30170         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30171             _this.fireEvent('mapRightClick', this, event);
30172             
30173         });
30174         
30175         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30176             _this.fireEvent('markerClick', this, event);
30177             
30178         });
30179
30180         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30181             _this.fireEvent('markerRightClick', this, event);
30182             
30183         });
30184         
30185         this.setPosition(this.gMapContext.location);
30186         
30187         this.fireEvent('initial', this, this.gMapContext.location);
30188     },
30189     
30190     initOverlayView: function()
30191     {
30192         var _this = this;
30193         
30194         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30195             
30196             draw: function()
30197             {
30198                 _this.fireEvent('OverlayViewDraw', _this);
30199             },
30200             
30201             onAdd: function()
30202             {
30203                 _this.fireEvent('OverlayViewOnAdd', _this);
30204             },
30205             
30206             onRemove: function()
30207             {
30208                 _this.fireEvent('OverlayViewOnRemove', _this);
30209             },
30210             
30211             show: function(cpx)
30212             {
30213                 _this.fireEvent('OverlayViewShow', _this, cpx);
30214             },
30215             
30216             hide: function()
30217             {
30218                 _this.fireEvent('OverlayViewHide', _this);
30219             }
30220             
30221         });
30222     },
30223     
30224     fromLatLngToContainerPixel: function(event)
30225     {
30226         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30227     },
30228     
30229     isApplied: function() 
30230     {
30231         return this.getGmapContext() == false ? false : true;
30232     },
30233     
30234     getGmapContext: function() 
30235     {
30236         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30237     },
30238     
30239     GMapContext: function() 
30240     {
30241         var position = new google.maps.LatLng(this.latitude, this.longitude);
30242         
30243         var _map = new google.maps.Map(this.el.dom, {
30244             center: position,
30245             zoom: this.zoom,
30246             mapTypeId: this.mapTypeId,
30247             mapTypeControl: this.mapTypeControl,
30248             disableDoubleClickZoom: this.disableDoubleClickZoom,
30249             scrollwheel: this.scrollwheel,
30250             streetViewControl: this.streetViewControl,
30251             locationName: this.locationName,
30252             draggable: this.draggable,
30253             enableAutocomplete: this.enableAutocomplete,
30254             enableReverseGeocode: this.enableReverseGeocode
30255         });
30256         
30257         var _marker = new google.maps.Marker({
30258             position: position,
30259             map: _map,
30260             title: this.markerTitle,
30261             draggable: this.draggable
30262         });
30263         
30264         return {
30265             map: _map,
30266             marker: _marker,
30267             circle: null,
30268             location: position,
30269             radius: this.radius,
30270             locationName: this.locationName,
30271             addressComponents: {
30272                 formatted_address: null,
30273                 addressLine1: null,
30274                 addressLine2: null,
30275                 streetName: null,
30276                 streetNumber: null,
30277                 city: null,
30278                 district: null,
30279                 state: null,
30280                 stateOrProvince: null
30281             },
30282             settings: this,
30283             domContainer: this.el.dom,
30284             geodecoder: new google.maps.Geocoder()
30285         };
30286     },
30287     
30288     drawCircle: function(center, radius, options) 
30289     {
30290         if (this.gMapContext.circle != null) {
30291             this.gMapContext.circle.setMap(null);
30292         }
30293         if (radius > 0) {
30294             radius *= 1;
30295             options = Roo.apply({}, options, {
30296                 strokeColor: "#0000FF",
30297                 strokeOpacity: .35,
30298                 strokeWeight: 2,
30299                 fillColor: "#0000FF",
30300                 fillOpacity: .2
30301             });
30302             
30303             options.map = this.gMapContext.map;
30304             options.radius = radius;
30305             options.center = center;
30306             this.gMapContext.circle = new google.maps.Circle(options);
30307             return this.gMapContext.circle;
30308         }
30309         
30310         return null;
30311     },
30312     
30313     setPosition: function(location) 
30314     {
30315         this.gMapContext.location = location;
30316         this.gMapContext.marker.setPosition(location);
30317         this.gMapContext.map.panTo(location);
30318         this.drawCircle(location, this.gMapContext.radius, {});
30319         
30320         var _this = this;
30321         
30322         if (this.gMapContext.settings.enableReverseGeocode) {
30323             this.gMapContext.geodecoder.geocode({
30324                 latLng: this.gMapContext.location
30325             }, function(results, status) {
30326                 
30327                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30328                     _this.gMapContext.locationName = results[0].formatted_address;
30329                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30330                     
30331                     _this.fireEvent('positionchanged', this, location);
30332                 }
30333             });
30334             
30335             return;
30336         }
30337         
30338         this.fireEvent('positionchanged', this, location);
30339     },
30340     
30341     resize: function()
30342     {
30343         google.maps.event.trigger(this.gMapContext.map, "resize");
30344         
30345         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30346         
30347         this.fireEvent('resize', this);
30348     },
30349     
30350     setPositionByLatLng: function(latitude, longitude)
30351     {
30352         this.setPosition(new google.maps.LatLng(latitude, longitude));
30353     },
30354     
30355     getCurrentPosition: function() 
30356     {
30357         return {
30358             latitude: this.gMapContext.location.lat(),
30359             longitude: this.gMapContext.location.lng()
30360         };
30361     },
30362     
30363     getAddressName: function() 
30364     {
30365         return this.gMapContext.locationName;
30366     },
30367     
30368     getAddressComponents: function() 
30369     {
30370         return this.gMapContext.addressComponents;
30371     },
30372     
30373     address_component_from_google_geocode: function(address_components) 
30374     {
30375         var result = {};
30376         
30377         for (var i = 0; i < address_components.length; i++) {
30378             var component = address_components[i];
30379             if (component.types.indexOf("postal_code") >= 0) {
30380                 result.postalCode = component.short_name;
30381             } else if (component.types.indexOf("street_number") >= 0) {
30382                 result.streetNumber = component.short_name;
30383             } else if (component.types.indexOf("route") >= 0) {
30384                 result.streetName = component.short_name;
30385             } else if (component.types.indexOf("neighborhood") >= 0) {
30386                 result.city = component.short_name;
30387             } else if (component.types.indexOf("locality") >= 0) {
30388                 result.city = component.short_name;
30389             } else if (component.types.indexOf("sublocality") >= 0) {
30390                 result.district = component.short_name;
30391             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30392                 result.stateOrProvince = component.short_name;
30393             } else if (component.types.indexOf("country") >= 0) {
30394                 result.country = component.short_name;
30395             }
30396         }
30397         
30398         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30399         result.addressLine2 = "";
30400         return result;
30401     },
30402     
30403     setZoomLevel: function(zoom)
30404     {
30405         this.gMapContext.map.setZoom(zoom);
30406     },
30407     
30408     show: function()
30409     {
30410         if(!this.el){
30411             return;
30412         }
30413         
30414         this.el.show();
30415         
30416         this.resize();
30417         
30418         this.fireEvent('show', this);
30419     },
30420     
30421     hide: function()
30422     {
30423         if(!this.el){
30424             return;
30425         }
30426         
30427         this.el.hide();
30428         
30429         this.fireEvent('hide', this);
30430     }
30431     
30432 });
30433
30434 Roo.apply(Roo.bootstrap.LocationPicker, {
30435     
30436     OverlayView : function(map, options)
30437     {
30438         options = options || {};
30439         
30440         this.setMap(map);
30441     }
30442     
30443     
30444 });/**
30445  * @class Roo.bootstrap.Alert
30446  * @extends Roo.bootstrap.Component
30447  * Bootstrap Alert class - shows an alert area box
30448  * eg
30449  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30450   Enter a valid email address
30451 </div>
30452  * @licence LGPL
30453  * @cfg {String} title The title of alert
30454  * @cfg {String} html The content of alert
30455  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30456  * @cfg {String} fa font-awesomeicon
30457  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30458  * @cfg {Boolean} close true to show a x closer
30459  * 
30460  * 
30461  * @constructor
30462  * Create a new alert
30463  * @param {Object} config The config object
30464  */
30465
30466
30467 Roo.bootstrap.Alert = function(config){
30468     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30469     
30470 };
30471
30472 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30473     
30474     title: '',
30475     html: '',
30476     weight: false,
30477     fa: false,
30478     faicon: false, // BC
30479     close : false,
30480     
30481     
30482     getAutoCreate : function()
30483     {
30484         
30485         var cfg = {
30486             tag : 'div',
30487             cls : 'alert',
30488             cn : [
30489                 {
30490                     tag: 'button',
30491                     type :  "button",
30492                     cls: "close",
30493                     html : '×',
30494                     style : this.close ? '' : 'display:none'
30495                 },
30496                 {
30497                     tag : 'i',
30498                     cls : 'roo-alert-icon'
30499                     
30500                 },
30501                 {
30502                     tag : 'b',
30503                     cls : 'roo-alert-title',
30504                     html : this.title
30505                 },
30506                 {
30507                     tag : 'span',
30508                     cls : 'roo-alert-text',
30509                     html : this.html
30510                 }
30511             ]
30512         };
30513         
30514         if(this.faicon){
30515             cfg.cn[0].cls += ' fa ' + this.faicon;
30516         }
30517         if(this.fa){
30518             cfg.cn[0].cls += ' fa ' + this.fa;
30519         }
30520         
30521         if(this.weight){
30522             cfg.cls += ' alert-' + this.weight;
30523         }
30524         
30525         return cfg;
30526     },
30527     
30528     initEvents: function() 
30529     {
30530         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30531         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30532         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30533         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30534         if (this.seconds > 0) {
30535             this.hide.defer(this.seconds, this);
30536         }
30537     },
30538     /**
30539      * Set the Title Message HTML
30540      * @param {String} html
30541      */
30542     setTitle : function(str)
30543     {
30544         this.titleEl.dom.innerHTML = str;
30545     },
30546      
30547      /**
30548      * Set the Body Message HTML
30549      * @param {String} html
30550      */
30551     setHtml : function(str)
30552     {
30553         this.htmlEl.dom.innerHTML = str;
30554     },
30555     /**
30556      * Set the Weight of the alert
30557      * @param {String} (success|info|warning|danger) weight
30558      */
30559     
30560     setWeight : function(weight)
30561     {
30562         if(this.weight){
30563             this.el.removeClass('alert-' + this.weight);
30564         }
30565         
30566         this.weight = weight;
30567         
30568         this.el.addClass('alert-' + this.weight);
30569     },
30570       /**
30571      * Set the Icon of the alert
30572      * @param {String} see fontawsome names (name without the 'fa-' bit)
30573      */
30574     setIcon : function(icon)
30575     {
30576         if(this.faicon){
30577             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30578         }
30579         
30580         this.faicon = icon;
30581         
30582         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30583     },
30584     /**
30585      * Hide the Alert
30586      */
30587     hide: function() 
30588     {
30589         this.el.hide();   
30590     },
30591     /**
30592      * Show the Alert
30593      */
30594     show: function() 
30595     {  
30596         this.el.show();   
30597     }
30598     
30599 });
30600
30601  
30602 /*
30603 * Licence: LGPL
30604 */
30605
30606 /**
30607  * @class Roo.bootstrap.UploadCropbox
30608  * @extends Roo.bootstrap.Component
30609  * Bootstrap UploadCropbox class
30610  * @cfg {String} emptyText show when image has been loaded
30611  * @cfg {String} rotateNotify show when image too small to rotate
30612  * @cfg {Number} errorTimeout default 3000
30613  * @cfg {Number} minWidth default 300
30614  * @cfg {Number} minHeight default 300
30615  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30616  * @cfg {Boolean} isDocument (true|false) default false
30617  * @cfg {String} url action url
30618  * @cfg {String} paramName default 'imageUpload'
30619  * @cfg {String} method default POST
30620  * @cfg {Boolean} loadMask (true|false) default true
30621  * @cfg {Boolean} loadingText default 'Loading...'
30622  * 
30623  * @constructor
30624  * Create a new UploadCropbox
30625  * @param {Object} config The config object
30626  */
30627
30628 Roo.bootstrap.UploadCropbox = function(config){
30629     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30630     
30631     this.addEvents({
30632         /**
30633          * @event beforeselectfile
30634          * Fire before select file
30635          * @param {Roo.bootstrap.UploadCropbox} this
30636          */
30637         "beforeselectfile" : true,
30638         /**
30639          * @event initial
30640          * Fire after initEvent
30641          * @param {Roo.bootstrap.UploadCropbox} this
30642          */
30643         "initial" : true,
30644         /**
30645          * @event crop
30646          * Fire after initEvent
30647          * @param {Roo.bootstrap.UploadCropbox} this
30648          * @param {String} data
30649          */
30650         "crop" : true,
30651         /**
30652          * @event prepare
30653          * Fire when preparing the file data
30654          * @param {Roo.bootstrap.UploadCropbox} this
30655          * @param {Object} file
30656          */
30657         "prepare" : true,
30658         /**
30659          * @event exception
30660          * Fire when get exception
30661          * @param {Roo.bootstrap.UploadCropbox} this
30662          * @param {XMLHttpRequest} xhr
30663          */
30664         "exception" : true,
30665         /**
30666          * @event beforeloadcanvas
30667          * Fire before load the canvas
30668          * @param {Roo.bootstrap.UploadCropbox} this
30669          * @param {String} src
30670          */
30671         "beforeloadcanvas" : true,
30672         /**
30673          * @event trash
30674          * Fire when trash image
30675          * @param {Roo.bootstrap.UploadCropbox} this
30676          */
30677         "trash" : true,
30678         /**
30679          * @event download
30680          * Fire when download the image
30681          * @param {Roo.bootstrap.UploadCropbox} this
30682          */
30683         "download" : true,
30684         /**
30685          * @event footerbuttonclick
30686          * Fire when footerbuttonclick
30687          * @param {Roo.bootstrap.UploadCropbox} this
30688          * @param {String} type
30689          */
30690         "footerbuttonclick" : true,
30691         /**
30692          * @event resize
30693          * Fire when resize
30694          * @param {Roo.bootstrap.UploadCropbox} this
30695          */
30696         "resize" : true,
30697         /**
30698          * @event rotate
30699          * Fire when rotate the image
30700          * @param {Roo.bootstrap.UploadCropbox} this
30701          * @param {String} pos
30702          */
30703         "rotate" : true,
30704         /**
30705          * @event inspect
30706          * Fire when inspect the file
30707          * @param {Roo.bootstrap.UploadCropbox} this
30708          * @param {Object} file
30709          */
30710         "inspect" : true,
30711         /**
30712          * @event upload
30713          * Fire when xhr upload the file
30714          * @param {Roo.bootstrap.UploadCropbox} this
30715          * @param {Object} data
30716          */
30717         "upload" : true,
30718         /**
30719          * @event arrange
30720          * Fire when arrange the file data
30721          * @param {Roo.bootstrap.UploadCropbox} this
30722          * @param {Object} formData
30723          */
30724         "arrange" : true
30725     });
30726     
30727     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30728 };
30729
30730 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30731     
30732     emptyText : 'Click to upload image',
30733     rotateNotify : 'Image is too small to rotate',
30734     errorTimeout : 3000,
30735     scale : 0,
30736     baseScale : 1,
30737     rotate : 0,
30738     dragable : false,
30739     pinching : false,
30740     mouseX : 0,
30741     mouseY : 0,
30742     cropData : false,
30743     minWidth : 300,
30744     minHeight : 300,
30745     file : false,
30746     exif : {},
30747     baseRotate : 1,
30748     cropType : 'image/jpeg',
30749     buttons : false,
30750     canvasLoaded : false,
30751     isDocument : false,
30752     method : 'POST',
30753     paramName : 'imageUpload',
30754     loadMask : true,
30755     loadingText : 'Loading...',
30756     maskEl : false,
30757     
30758     getAutoCreate : function()
30759     {
30760         var cfg = {
30761             tag : 'div',
30762             cls : 'roo-upload-cropbox',
30763             cn : [
30764                 {
30765                     tag : 'input',
30766                     cls : 'roo-upload-cropbox-selector',
30767                     type : 'file'
30768                 },
30769                 {
30770                     tag : 'div',
30771                     cls : 'roo-upload-cropbox-body',
30772                     style : 'cursor:pointer',
30773                     cn : [
30774                         {
30775                             tag : 'div',
30776                             cls : 'roo-upload-cropbox-preview'
30777                         },
30778                         {
30779                             tag : 'div',
30780                             cls : 'roo-upload-cropbox-thumb'
30781                         },
30782                         {
30783                             tag : 'div',
30784                             cls : 'roo-upload-cropbox-empty-notify',
30785                             html : this.emptyText
30786                         },
30787                         {
30788                             tag : 'div',
30789                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30790                             html : this.rotateNotify
30791                         }
30792                     ]
30793                 },
30794                 {
30795                     tag : 'div',
30796                     cls : 'roo-upload-cropbox-footer',
30797                     cn : {
30798                         tag : 'div',
30799                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30800                         cn : []
30801                     }
30802                 }
30803             ]
30804         };
30805         
30806         return cfg;
30807     },
30808     
30809     onRender : function(ct, position)
30810     {
30811         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30812         
30813         if (this.buttons.length) {
30814             
30815             Roo.each(this.buttons, function(bb) {
30816                 
30817                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30818                 
30819                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30820                 
30821             }, this);
30822         }
30823         
30824         if(this.loadMask){
30825             this.maskEl = this.el;
30826         }
30827     },
30828     
30829     initEvents : function()
30830     {
30831         this.urlAPI = (window.createObjectURL && window) || 
30832                                 (window.URL && URL.revokeObjectURL && URL) || 
30833                                 (window.webkitURL && webkitURL);
30834                         
30835         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30836         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30837         
30838         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30839         this.selectorEl.hide();
30840         
30841         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30842         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30843         
30844         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30845         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30846         this.thumbEl.hide();
30847         
30848         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30849         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30850         
30851         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30852         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30853         this.errorEl.hide();
30854         
30855         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30856         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30857         this.footerEl.hide();
30858         
30859         this.setThumbBoxSize();
30860         
30861         this.bind();
30862         
30863         this.resize();
30864         
30865         this.fireEvent('initial', this);
30866     },
30867
30868     bind : function()
30869     {
30870         var _this = this;
30871         
30872         window.addEventListener("resize", function() { _this.resize(); } );
30873         
30874         this.bodyEl.on('click', this.beforeSelectFile, this);
30875         
30876         if(Roo.isTouch){
30877             this.bodyEl.on('touchstart', this.onTouchStart, this);
30878             this.bodyEl.on('touchmove', this.onTouchMove, this);
30879             this.bodyEl.on('touchend', this.onTouchEnd, this);
30880         }
30881         
30882         if(!Roo.isTouch){
30883             this.bodyEl.on('mousedown', this.onMouseDown, this);
30884             this.bodyEl.on('mousemove', this.onMouseMove, this);
30885             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30886             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30887             Roo.get(document).on('mouseup', this.onMouseUp, this);
30888         }
30889         
30890         this.selectorEl.on('change', this.onFileSelected, this);
30891     },
30892     
30893     reset : function()
30894     {    
30895         this.scale = 0;
30896         this.baseScale = 1;
30897         this.rotate = 0;
30898         this.baseRotate = 1;
30899         this.dragable = false;
30900         this.pinching = false;
30901         this.mouseX = 0;
30902         this.mouseY = 0;
30903         this.cropData = false;
30904         this.notifyEl.dom.innerHTML = this.emptyText;
30905         
30906         this.selectorEl.dom.value = '';
30907         
30908     },
30909     
30910     resize : function()
30911     {
30912         if(this.fireEvent('resize', this) != false){
30913             this.setThumbBoxPosition();
30914             this.setCanvasPosition();
30915         }
30916     },
30917     
30918     onFooterButtonClick : function(e, el, o, type)
30919     {
30920         switch (type) {
30921             case 'rotate-left' :
30922                 this.onRotateLeft(e);
30923                 break;
30924             case 'rotate-right' :
30925                 this.onRotateRight(e);
30926                 break;
30927             case 'picture' :
30928                 this.beforeSelectFile(e);
30929                 break;
30930             case 'trash' :
30931                 this.trash(e);
30932                 break;
30933             case 'crop' :
30934                 this.crop(e);
30935                 break;
30936             case 'download' :
30937                 this.download(e);
30938                 break;
30939             default :
30940                 break;
30941         }
30942         
30943         this.fireEvent('footerbuttonclick', this, type);
30944     },
30945     
30946     beforeSelectFile : function(e)
30947     {
30948         e.preventDefault();
30949         
30950         if(this.fireEvent('beforeselectfile', this) != false){
30951             this.selectorEl.dom.click();
30952         }
30953     },
30954     
30955     onFileSelected : function(e)
30956     {
30957         e.preventDefault();
30958         
30959         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30960             return;
30961         }
30962         
30963         var file = this.selectorEl.dom.files[0];
30964         
30965         if(this.fireEvent('inspect', this, file) != false){
30966             this.prepare(file);
30967         }
30968         
30969     },
30970     
30971     trash : function(e)
30972     {
30973         this.fireEvent('trash', this);
30974     },
30975     
30976     download : function(e)
30977     {
30978         this.fireEvent('download', this);
30979     },
30980     
30981     loadCanvas : function(src)
30982     {   
30983         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30984             
30985             this.reset();
30986             
30987             this.imageEl = document.createElement('img');
30988             
30989             var _this = this;
30990             
30991             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30992             
30993             this.imageEl.src = src;
30994         }
30995     },
30996     
30997     onLoadCanvas : function()
30998     {   
30999         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31000         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31001         
31002         this.bodyEl.un('click', this.beforeSelectFile, this);
31003         
31004         this.notifyEl.hide();
31005         this.thumbEl.show();
31006         this.footerEl.show();
31007         
31008         this.baseRotateLevel();
31009         
31010         if(this.isDocument){
31011             this.setThumbBoxSize();
31012         }
31013         
31014         this.setThumbBoxPosition();
31015         
31016         this.baseScaleLevel();
31017         
31018         this.draw();
31019         
31020         this.resize();
31021         
31022         this.canvasLoaded = true;
31023         
31024         if(this.loadMask){
31025             this.maskEl.unmask();
31026         }
31027         
31028     },
31029     
31030     setCanvasPosition : function()
31031     {   
31032         if(!this.canvasEl){
31033             return;
31034         }
31035         
31036         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31037         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31038         
31039         this.previewEl.setLeft(pw);
31040         this.previewEl.setTop(ph);
31041         
31042     },
31043     
31044     onMouseDown : function(e)
31045     {   
31046         e.stopEvent();
31047         
31048         this.dragable = true;
31049         this.pinching = false;
31050         
31051         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31052             this.dragable = false;
31053             return;
31054         }
31055         
31056         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31057         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31058         
31059     },
31060     
31061     onMouseMove : function(e)
31062     {   
31063         e.stopEvent();
31064         
31065         if(!this.canvasLoaded){
31066             return;
31067         }
31068         
31069         if (!this.dragable){
31070             return;
31071         }
31072         
31073         var minX = Math.ceil(this.thumbEl.getLeft(true));
31074         var minY = Math.ceil(this.thumbEl.getTop(true));
31075         
31076         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31077         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31078         
31079         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31080         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31081         
31082         x = x - this.mouseX;
31083         y = y - this.mouseY;
31084         
31085         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31086         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31087         
31088         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31089         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31090         
31091         this.previewEl.setLeft(bgX);
31092         this.previewEl.setTop(bgY);
31093         
31094         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31095         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31096     },
31097     
31098     onMouseUp : function(e)
31099     {   
31100         e.stopEvent();
31101         
31102         this.dragable = false;
31103     },
31104     
31105     onMouseWheel : function(e)
31106     {   
31107         e.stopEvent();
31108         
31109         this.startScale = this.scale;
31110         
31111         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31112         
31113         if(!this.zoomable()){
31114             this.scale = this.startScale;
31115             return;
31116         }
31117         
31118         this.draw();
31119         
31120         return;
31121     },
31122     
31123     zoomable : function()
31124     {
31125         var minScale = this.thumbEl.getWidth() / this.minWidth;
31126         
31127         if(this.minWidth < this.minHeight){
31128             minScale = this.thumbEl.getHeight() / this.minHeight;
31129         }
31130         
31131         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31132         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31133         
31134         if(
31135                 this.isDocument &&
31136                 (this.rotate == 0 || this.rotate == 180) && 
31137                 (
31138                     width > this.imageEl.OriginWidth || 
31139                     height > this.imageEl.OriginHeight ||
31140                     (width < this.minWidth && height < this.minHeight)
31141                 )
31142         ){
31143             return false;
31144         }
31145         
31146         if(
31147                 this.isDocument &&
31148                 (this.rotate == 90 || this.rotate == 270) && 
31149                 (
31150                     width > this.imageEl.OriginWidth || 
31151                     height > this.imageEl.OriginHeight ||
31152                     (width < this.minHeight && height < this.minWidth)
31153                 )
31154         ){
31155             return false;
31156         }
31157         
31158         if(
31159                 !this.isDocument &&
31160                 (this.rotate == 0 || this.rotate == 180) && 
31161                 (
31162                     width < this.minWidth || 
31163                     width > this.imageEl.OriginWidth || 
31164                     height < this.minHeight || 
31165                     height > this.imageEl.OriginHeight
31166                 )
31167         ){
31168             return false;
31169         }
31170         
31171         if(
31172                 !this.isDocument &&
31173                 (this.rotate == 90 || this.rotate == 270) && 
31174                 (
31175                     width < this.minHeight || 
31176                     width > this.imageEl.OriginWidth || 
31177                     height < this.minWidth || 
31178                     height > this.imageEl.OriginHeight
31179                 )
31180         ){
31181             return false;
31182         }
31183         
31184         return true;
31185         
31186     },
31187     
31188     onRotateLeft : function(e)
31189     {   
31190         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31191             
31192             var minScale = this.thumbEl.getWidth() / this.minWidth;
31193             
31194             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31195             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31196             
31197             this.startScale = this.scale;
31198             
31199             while (this.getScaleLevel() < minScale){
31200             
31201                 this.scale = this.scale + 1;
31202                 
31203                 if(!this.zoomable()){
31204                     break;
31205                 }
31206                 
31207                 if(
31208                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31209                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31210                 ){
31211                     continue;
31212                 }
31213                 
31214                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31215
31216                 this.draw();
31217                 
31218                 return;
31219             }
31220             
31221             this.scale = this.startScale;
31222             
31223             this.onRotateFail();
31224             
31225             return false;
31226         }
31227         
31228         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31229
31230         if(this.isDocument){
31231             this.setThumbBoxSize();
31232             this.setThumbBoxPosition();
31233             this.setCanvasPosition();
31234         }
31235         
31236         this.draw();
31237         
31238         this.fireEvent('rotate', this, 'left');
31239         
31240     },
31241     
31242     onRotateRight : function(e)
31243     {
31244         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31245             
31246             var minScale = this.thumbEl.getWidth() / this.minWidth;
31247         
31248             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31249             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31250             
31251             this.startScale = this.scale;
31252             
31253             while (this.getScaleLevel() < minScale){
31254             
31255                 this.scale = this.scale + 1;
31256                 
31257                 if(!this.zoomable()){
31258                     break;
31259                 }
31260                 
31261                 if(
31262                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31263                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31264                 ){
31265                     continue;
31266                 }
31267                 
31268                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31269
31270                 this.draw();
31271                 
31272                 return;
31273             }
31274             
31275             this.scale = this.startScale;
31276             
31277             this.onRotateFail();
31278             
31279             return false;
31280         }
31281         
31282         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31283
31284         if(this.isDocument){
31285             this.setThumbBoxSize();
31286             this.setThumbBoxPosition();
31287             this.setCanvasPosition();
31288         }
31289         
31290         this.draw();
31291         
31292         this.fireEvent('rotate', this, 'right');
31293     },
31294     
31295     onRotateFail : function()
31296     {
31297         this.errorEl.show(true);
31298         
31299         var _this = this;
31300         
31301         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31302     },
31303     
31304     draw : function()
31305     {
31306         this.previewEl.dom.innerHTML = '';
31307         
31308         var canvasEl = document.createElement("canvas");
31309         
31310         var contextEl = canvasEl.getContext("2d");
31311         
31312         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31313         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31314         var center = this.imageEl.OriginWidth / 2;
31315         
31316         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31317             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31318             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31319             center = this.imageEl.OriginHeight / 2;
31320         }
31321         
31322         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31323         
31324         contextEl.translate(center, center);
31325         contextEl.rotate(this.rotate * Math.PI / 180);
31326
31327         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31328         
31329         this.canvasEl = document.createElement("canvas");
31330         
31331         this.contextEl = this.canvasEl.getContext("2d");
31332         
31333         switch (this.rotate) {
31334             case 0 :
31335                 
31336                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31337                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31338                 
31339                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31340                 
31341                 break;
31342             case 90 : 
31343                 
31344                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31345                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31346                 
31347                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31348                     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);
31349                     break;
31350                 }
31351                 
31352                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31353                 
31354                 break;
31355             case 180 :
31356                 
31357                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31358                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31359                 
31360                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31361                     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);
31362                     break;
31363                 }
31364                 
31365                 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);
31366                 
31367                 break;
31368             case 270 :
31369                 
31370                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31371                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31372         
31373                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31374                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31375                     break;
31376                 }
31377                 
31378                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31379                 
31380                 break;
31381             default : 
31382                 break;
31383         }
31384         
31385         this.previewEl.appendChild(this.canvasEl);
31386         
31387         this.setCanvasPosition();
31388     },
31389     
31390     crop : function()
31391     {
31392         if(!this.canvasLoaded){
31393             return;
31394         }
31395         
31396         var imageCanvas = document.createElement("canvas");
31397         
31398         var imageContext = imageCanvas.getContext("2d");
31399         
31400         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31401         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31402         
31403         var center = imageCanvas.width / 2;
31404         
31405         imageContext.translate(center, center);
31406         
31407         imageContext.rotate(this.rotate * Math.PI / 180);
31408         
31409         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31410         
31411         var canvas = document.createElement("canvas");
31412         
31413         var context = canvas.getContext("2d");
31414                 
31415         canvas.width = this.minWidth;
31416         canvas.height = this.minHeight;
31417
31418         switch (this.rotate) {
31419             case 0 :
31420                 
31421                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31422                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31423                 
31424                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31425                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31426                 
31427                 var targetWidth = this.minWidth - 2 * x;
31428                 var targetHeight = this.minHeight - 2 * y;
31429                 
31430                 var scale = 1;
31431                 
31432                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31433                     scale = targetWidth / width;
31434                 }
31435                 
31436                 if(x > 0 && y == 0){
31437                     scale = targetHeight / height;
31438                 }
31439                 
31440                 if(x > 0 && y > 0){
31441                     scale = targetWidth / width;
31442                     
31443                     if(width < height){
31444                         scale = targetHeight / height;
31445                     }
31446                 }
31447                 
31448                 context.scale(scale, scale);
31449                 
31450                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31451                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31452
31453                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31454                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31455
31456                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31457                 
31458                 break;
31459             case 90 : 
31460                 
31461                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31462                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31463                 
31464                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31465                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31466                 
31467                 var targetWidth = this.minWidth - 2 * x;
31468                 var targetHeight = this.minHeight - 2 * y;
31469                 
31470                 var scale = 1;
31471                 
31472                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31473                     scale = targetWidth / width;
31474                 }
31475                 
31476                 if(x > 0 && y == 0){
31477                     scale = targetHeight / height;
31478                 }
31479                 
31480                 if(x > 0 && y > 0){
31481                     scale = targetWidth / width;
31482                     
31483                     if(width < height){
31484                         scale = targetHeight / height;
31485                     }
31486                 }
31487                 
31488                 context.scale(scale, scale);
31489                 
31490                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31491                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31492
31493                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31494                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31495                 
31496                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31497                 
31498                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31499                 
31500                 break;
31501             case 180 :
31502                 
31503                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31504                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31505                 
31506                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31507                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31508                 
31509                 var targetWidth = this.minWidth - 2 * x;
31510                 var targetHeight = this.minHeight - 2 * y;
31511                 
31512                 var scale = 1;
31513                 
31514                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31515                     scale = targetWidth / width;
31516                 }
31517                 
31518                 if(x > 0 && y == 0){
31519                     scale = targetHeight / height;
31520                 }
31521                 
31522                 if(x > 0 && y > 0){
31523                     scale = targetWidth / width;
31524                     
31525                     if(width < height){
31526                         scale = targetHeight / height;
31527                     }
31528                 }
31529                 
31530                 context.scale(scale, scale);
31531                 
31532                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31533                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31534
31535                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31536                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31537
31538                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31539                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31540                 
31541                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31542                 
31543                 break;
31544             case 270 :
31545                 
31546                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31547                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31548                 
31549                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31550                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31551                 
31552                 var targetWidth = this.minWidth - 2 * x;
31553                 var targetHeight = this.minHeight - 2 * y;
31554                 
31555                 var scale = 1;
31556                 
31557                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31558                     scale = targetWidth / width;
31559                 }
31560                 
31561                 if(x > 0 && y == 0){
31562                     scale = targetHeight / height;
31563                 }
31564                 
31565                 if(x > 0 && y > 0){
31566                     scale = targetWidth / width;
31567                     
31568                     if(width < height){
31569                         scale = targetHeight / height;
31570                     }
31571                 }
31572                 
31573                 context.scale(scale, scale);
31574                 
31575                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31576                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31577
31578                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31579                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31580                 
31581                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31582                 
31583                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31584                 
31585                 break;
31586             default : 
31587                 break;
31588         }
31589         
31590         this.cropData = canvas.toDataURL(this.cropType);
31591         
31592         if(this.fireEvent('crop', this, this.cropData) !== false){
31593             this.process(this.file, this.cropData);
31594         }
31595         
31596         return;
31597         
31598     },
31599     
31600     setThumbBoxSize : function()
31601     {
31602         var width, height;
31603         
31604         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31605             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31606             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31607             
31608             this.minWidth = width;
31609             this.minHeight = height;
31610             
31611             if(this.rotate == 90 || this.rotate == 270){
31612                 this.minWidth = height;
31613                 this.minHeight = width;
31614             }
31615         }
31616         
31617         height = 300;
31618         width = Math.ceil(this.minWidth * height / this.minHeight);
31619         
31620         if(this.minWidth > this.minHeight){
31621             width = 300;
31622             height = Math.ceil(this.minHeight * width / this.minWidth);
31623         }
31624         
31625         this.thumbEl.setStyle({
31626             width : width + 'px',
31627             height : height + 'px'
31628         });
31629
31630         return;
31631             
31632     },
31633     
31634     setThumbBoxPosition : function()
31635     {
31636         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31637         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31638         
31639         this.thumbEl.setLeft(x);
31640         this.thumbEl.setTop(y);
31641         
31642     },
31643     
31644     baseRotateLevel : function()
31645     {
31646         this.baseRotate = 1;
31647         
31648         if(
31649                 typeof(this.exif) != 'undefined' &&
31650                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31651                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31652         ){
31653             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31654         }
31655         
31656         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31657         
31658     },
31659     
31660     baseScaleLevel : function()
31661     {
31662         var width, height;
31663         
31664         if(this.isDocument){
31665             
31666             if(this.baseRotate == 6 || this.baseRotate == 8){
31667             
31668                 height = this.thumbEl.getHeight();
31669                 this.baseScale = height / this.imageEl.OriginWidth;
31670
31671                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31672                     width = this.thumbEl.getWidth();
31673                     this.baseScale = width / this.imageEl.OriginHeight;
31674                 }
31675
31676                 return;
31677             }
31678
31679             height = this.thumbEl.getHeight();
31680             this.baseScale = height / this.imageEl.OriginHeight;
31681
31682             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31683                 width = this.thumbEl.getWidth();
31684                 this.baseScale = width / this.imageEl.OriginWidth;
31685             }
31686
31687             return;
31688         }
31689         
31690         if(this.baseRotate == 6 || this.baseRotate == 8){
31691             
31692             width = this.thumbEl.getHeight();
31693             this.baseScale = width / this.imageEl.OriginHeight;
31694             
31695             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31696                 height = this.thumbEl.getWidth();
31697                 this.baseScale = height / this.imageEl.OriginHeight;
31698             }
31699             
31700             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31701                 height = this.thumbEl.getWidth();
31702                 this.baseScale = height / this.imageEl.OriginHeight;
31703                 
31704                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31705                     width = this.thumbEl.getHeight();
31706                     this.baseScale = width / this.imageEl.OriginWidth;
31707                 }
31708             }
31709             
31710             return;
31711         }
31712         
31713         width = this.thumbEl.getWidth();
31714         this.baseScale = width / this.imageEl.OriginWidth;
31715         
31716         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31717             height = this.thumbEl.getHeight();
31718             this.baseScale = height / this.imageEl.OriginHeight;
31719         }
31720         
31721         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31722             
31723             height = this.thumbEl.getHeight();
31724             this.baseScale = height / this.imageEl.OriginHeight;
31725             
31726             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31727                 width = this.thumbEl.getWidth();
31728                 this.baseScale = width / this.imageEl.OriginWidth;
31729             }
31730             
31731         }
31732         
31733         return;
31734     },
31735     
31736     getScaleLevel : function()
31737     {
31738         return this.baseScale * Math.pow(1.1, this.scale);
31739     },
31740     
31741     onTouchStart : function(e)
31742     {
31743         if(!this.canvasLoaded){
31744             this.beforeSelectFile(e);
31745             return;
31746         }
31747         
31748         var touches = e.browserEvent.touches;
31749         
31750         if(!touches){
31751             return;
31752         }
31753         
31754         if(touches.length == 1){
31755             this.onMouseDown(e);
31756             return;
31757         }
31758         
31759         if(touches.length != 2){
31760             return;
31761         }
31762         
31763         var coords = [];
31764         
31765         for(var i = 0, finger; finger = touches[i]; i++){
31766             coords.push(finger.pageX, finger.pageY);
31767         }
31768         
31769         var x = Math.pow(coords[0] - coords[2], 2);
31770         var y = Math.pow(coords[1] - coords[3], 2);
31771         
31772         this.startDistance = Math.sqrt(x + y);
31773         
31774         this.startScale = this.scale;
31775         
31776         this.pinching = true;
31777         this.dragable = false;
31778         
31779     },
31780     
31781     onTouchMove : function(e)
31782     {
31783         if(!this.pinching && !this.dragable){
31784             return;
31785         }
31786         
31787         var touches = e.browserEvent.touches;
31788         
31789         if(!touches){
31790             return;
31791         }
31792         
31793         if(this.dragable){
31794             this.onMouseMove(e);
31795             return;
31796         }
31797         
31798         var coords = [];
31799         
31800         for(var i = 0, finger; finger = touches[i]; i++){
31801             coords.push(finger.pageX, finger.pageY);
31802         }
31803         
31804         var x = Math.pow(coords[0] - coords[2], 2);
31805         var y = Math.pow(coords[1] - coords[3], 2);
31806         
31807         this.endDistance = Math.sqrt(x + y);
31808         
31809         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31810         
31811         if(!this.zoomable()){
31812             this.scale = this.startScale;
31813             return;
31814         }
31815         
31816         this.draw();
31817         
31818     },
31819     
31820     onTouchEnd : function(e)
31821     {
31822         this.pinching = false;
31823         this.dragable = false;
31824         
31825     },
31826     
31827     process : function(file, crop)
31828     {
31829         if(this.loadMask){
31830             this.maskEl.mask(this.loadingText);
31831         }
31832         
31833         this.xhr = new XMLHttpRequest();
31834         
31835         file.xhr = this.xhr;
31836
31837         this.xhr.open(this.method, this.url, true);
31838         
31839         var headers = {
31840             "Accept": "application/json",
31841             "Cache-Control": "no-cache",
31842             "X-Requested-With": "XMLHttpRequest"
31843         };
31844         
31845         for (var headerName in headers) {
31846             var headerValue = headers[headerName];
31847             if (headerValue) {
31848                 this.xhr.setRequestHeader(headerName, headerValue);
31849             }
31850         }
31851         
31852         var _this = this;
31853         
31854         this.xhr.onload = function()
31855         {
31856             _this.xhrOnLoad(_this.xhr);
31857         }
31858         
31859         this.xhr.onerror = function()
31860         {
31861             _this.xhrOnError(_this.xhr);
31862         }
31863         
31864         var formData = new FormData();
31865
31866         formData.append('returnHTML', 'NO');
31867         
31868         if(crop){
31869             formData.append('crop', crop);
31870         }
31871         
31872         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31873             formData.append(this.paramName, file, file.name);
31874         }
31875         
31876         if(typeof(file.filename) != 'undefined'){
31877             formData.append('filename', file.filename);
31878         }
31879         
31880         if(typeof(file.mimetype) != 'undefined'){
31881             formData.append('mimetype', file.mimetype);
31882         }
31883         
31884         if(this.fireEvent('arrange', this, formData) != false){
31885             this.xhr.send(formData);
31886         };
31887     },
31888     
31889     xhrOnLoad : function(xhr)
31890     {
31891         if(this.loadMask){
31892             this.maskEl.unmask();
31893         }
31894         
31895         if (xhr.readyState !== 4) {
31896             this.fireEvent('exception', this, xhr);
31897             return;
31898         }
31899
31900         var response = Roo.decode(xhr.responseText);
31901         
31902         if(!response.success){
31903             this.fireEvent('exception', this, xhr);
31904             return;
31905         }
31906         
31907         var response = Roo.decode(xhr.responseText);
31908         
31909         this.fireEvent('upload', this, response);
31910         
31911     },
31912     
31913     xhrOnError : function()
31914     {
31915         if(this.loadMask){
31916             this.maskEl.unmask();
31917         }
31918         
31919         Roo.log('xhr on error');
31920         
31921         var response = Roo.decode(xhr.responseText);
31922           
31923         Roo.log(response);
31924         
31925     },
31926     
31927     prepare : function(file)
31928     {   
31929         if(this.loadMask){
31930             this.maskEl.mask(this.loadingText);
31931         }
31932         
31933         this.file = false;
31934         this.exif = {};
31935         
31936         if(typeof(file) === 'string'){
31937             this.loadCanvas(file);
31938             return;
31939         }
31940         
31941         if(!file || !this.urlAPI){
31942             return;
31943         }
31944         
31945         this.file = file;
31946         this.cropType = file.type;
31947         
31948         var _this = this;
31949         
31950         if(this.fireEvent('prepare', this, this.file) != false){
31951             
31952             var reader = new FileReader();
31953             
31954             reader.onload = function (e) {
31955                 if (e.target.error) {
31956                     Roo.log(e.target.error);
31957                     return;
31958                 }
31959                 
31960                 var buffer = e.target.result,
31961                     dataView = new DataView(buffer),
31962                     offset = 2,
31963                     maxOffset = dataView.byteLength - 4,
31964                     markerBytes,
31965                     markerLength;
31966                 
31967                 if (dataView.getUint16(0) === 0xffd8) {
31968                     while (offset < maxOffset) {
31969                         markerBytes = dataView.getUint16(offset);
31970                         
31971                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31972                             markerLength = dataView.getUint16(offset + 2) + 2;
31973                             if (offset + markerLength > dataView.byteLength) {
31974                                 Roo.log('Invalid meta data: Invalid segment size.');
31975                                 break;
31976                             }
31977                             
31978                             if(markerBytes == 0xffe1){
31979                                 _this.parseExifData(
31980                                     dataView,
31981                                     offset,
31982                                     markerLength
31983                                 );
31984                             }
31985                             
31986                             offset += markerLength;
31987                             
31988                             continue;
31989                         }
31990                         
31991                         break;
31992                     }
31993                     
31994                 }
31995                 
31996                 var url = _this.urlAPI.createObjectURL(_this.file);
31997                 
31998                 _this.loadCanvas(url);
31999                 
32000                 return;
32001             }
32002             
32003             reader.readAsArrayBuffer(this.file);
32004             
32005         }
32006         
32007     },
32008     
32009     parseExifData : function(dataView, offset, length)
32010     {
32011         var tiffOffset = offset + 10,
32012             littleEndian,
32013             dirOffset;
32014     
32015         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32016             // No Exif data, might be XMP data instead
32017             return;
32018         }
32019         
32020         // Check for the ASCII code for "Exif" (0x45786966):
32021         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32022             // No Exif data, might be XMP data instead
32023             return;
32024         }
32025         if (tiffOffset + 8 > dataView.byteLength) {
32026             Roo.log('Invalid Exif data: Invalid segment size.');
32027             return;
32028         }
32029         // Check for the two null bytes:
32030         if (dataView.getUint16(offset + 8) !== 0x0000) {
32031             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32032             return;
32033         }
32034         // Check the byte alignment:
32035         switch (dataView.getUint16(tiffOffset)) {
32036         case 0x4949:
32037             littleEndian = true;
32038             break;
32039         case 0x4D4D:
32040             littleEndian = false;
32041             break;
32042         default:
32043             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32044             return;
32045         }
32046         // Check for the TIFF tag marker (0x002A):
32047         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32048             Roo.log('Invalid Exif data: Missing TIFF marker.');
32049             return;
32050         }
32051         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32052         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32053         
32054         this.parseExifTags(
32055             dataView,
32056             tiffOffset,
32057             tiffOffset + dirOffset,
32058             littleEndian
32059         );
32060     },
32061     
32062     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32063     {
32064         var tagsNumber,
32065             dirEndOffset,
32066             i;
32067         if (dirOffset + 6 > dataView.byteLength) {
32068             Roo.log('Invalid Exif data: Invalid directory offset.');
32069             return;
32070         }
32071         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32072         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32073         if (dirEndOffset + 4 > dataView.byteLength) {
32074             Roo.log('Invalid Exif data: Invalid directory size.');
32075             return;
32076         }
32077         for (i = 0; i < tagsNumber; i += 1) {
32078             this.parseExifTag(
32079                 dataView,
32080                 tiffOffset,
32081                 dirOffset + 2 + 12 * i, // tag offset
32082                 littleEndian
32083             );
32084         }
32085         // Return the offset to the next directory:
32086         return dataView.getUint32(dirEndOffset, littleEndian);
32087     },
32088     
32089     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32090     {
32091         var tag = dataView.getUint16(offset, littleEndian);
32092         
32093         this.exif[tag] = this.getExifValue(
32094             dataView,
32095             tiffOffset,
32096             offset,
32097             dataView.getUint16(offset + 2, littleEndian), // tag type
32098             dataView.getUint32(offset + 4, littleEndian), // tag length
32099             littleEndian
32100         );
32101     },
32102     
32103     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32104     {
32105         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32106             tagSize,
32107             dataOffset,
32108             values,
32109             i,
32110             str,
32111             c;
32112     
32113         if (!tagType) {
32114             Roo.log('Invalid Exif data: Invalid tag type.');
32115             return;
32116         }
32117         
32118         tagSize = tagType.size * length;
32119         // Determine if the value is contained in the dataOffset bytes,
32120         // or if the value at the dataOffset is a pointer to the actual data:
32121         dataOffset = tagSize > 4 ?
32122                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32123         if (dataOffset + tagSize > dataView.byteLength) {
32124             Roo.log('Invalid Exif data: Invalid data offset.');
32125             return;
32126         }
32127         if (length === 1) {
32128             return tagType.getValue(dataView, dataOffset, littleEndian);
32129         }
32130         values = [];
32131         for (i = 0; i < length; i += 1) {
32132             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32133         }
32134         
32135         if (tagType.ascii) {
32136             str = '';
32137             // Concatenate the chars:
32138             for (i = 0; i < values.length; i += 1) {
32139                 c = values[i];
32140                 // Ignore the terminating NULL byte(s):
32141                 if (c === '\u0000') {
32142                     break;
32143                 }
32144                 str += c;
32145             }
32146             return str;
32147         }
32148         return values;
32149     }
32150     
32151 });
32152
32153 Roo.apply(Roo.bootstrap.UploadCropbox, {
32154     tags : {
32155         'Orientation': 0x0112
32156     },
32157     
32158     Orientation: {
32159             1: 0, //'top-left',
32160 //            2: 'top-right',
32161             3: 180, //'bottom-right',
32162 //            4: 'bottom-left',
32163 //            5: 'left-top',
32164             6: 90, //'right-top',
32165 //            7: 'right-bottom',
32166             8: 270 //'left-bottom'
32167     },
32168     
32169     exifTagTypes : {
32170         // byte, 8-bit unsigned int:
32171         1: {
32172             getValue: function (dataView, dataOffset) {
32173                 return dataView.getUint8(dataOffset);
32174             },
32175             size: 1
32176         },
32177         // ascii, 8-bit byte:
32178         2: {
32179             getValue: function (dataView, dataOffset) {
32180                 return String.fromCharCode(dataView.getUint8(dataOffset));
32181             },
32182             size: 1,
32183             ascii: true
32184         },
32185         // short, 16 bit int:
32186         3: {
32187             getValue: function (dataView, dataOffset, littleEndian) {
32188                 return dataView.getUint16(dataOffset, littleEndian);
32189             },
32190             size: 2
32191         },
32192         // long, 32 bit int:
32193         4: {
32194             getValue: function (dataView, dataOffset, littleEndian) {
32195                 return dataView.getUint32(dataOffset, littleEndian);
32196             },
32197             size: 4
32198         },
32199         // rational = two long values, first is numerator, second is denominator:
32200         5: {
32201             getValue: function (dataView, dataOffset, littleEndian) {
32202                 return dataView.getUint32(dataOffset, littleEndian) /
32203                     dataView.getUint32(dataOffset + 4, littleEndian);
32204             },
32205             size: 8
32206         },
32207         // slong, 32 bit signed int:
32208         9: {
32209             getValue: function (dataView, dataOffset, littleEndian) {
32210                 return dataView.getInt32(dataOffset, littleEndian);
32211             },
32212             size: 4
32213         },
32214         // srational, two slongs, first is numerator, second is denominator:
32215         10: {
32216             getValue: function (dataView, dataOffset, littleEndian) {
32217                 return dataView.getInt32(dataOffset, littleEndian) /
32218                     dataView.getInt32(dataOffset + 4, littleEndian);
32219             },
32220             size: 8
32221         }
32222     },
32223     
32224     footer : {
32225         STANDARD : [
32226             {
32227                 tag : 'div',
32228                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32229                 action : 'rotate-left',
32230                 cn : [
32231                     {
32232                         tag : 'button',
32233                         cls : 'btn btn-default',
32234                         html : '<i class="fa fa-undo"></i>'
32235                     }
32236                 ]
32237             },
32238             {
32239                 tag : 'div',
32240                 cls : 'btn-group roo-upload-cropbox-picture',
32241                 action : 'picture',
32242                 cn : [
32243                     {
32244                         tag : 'button',
32245                         cls : 'btn btn-default',
32246                         html : '<i class="fa fa-picture-o"></i>'
32247                     }
32248                 ]
32249             },
32250             {
32251                 tag : 'div',
32252                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32253                 action : 'rotate-right',
32254                 cn : [
32255                     {
32256                         tag : 'button',
32257                         cls : 'btn btn-default',
32258                         html : '<i class="fa fa-repeat"></i>'
32259                     }
32260                 ]
32261             }
32262         ],
32263         DOCUMENT : [
32264             {
32265                 tag : 'div',
32266                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32267                 action : 'rotate-left',
32268                 cn : [
32269                     {
32270                         tag : 'button',
32271                         cls : 'btn btn-default',
32272                         html : '<i class="fa fa-undo"></i>'
32273                     }
32274                 ]
32275             },
32276             {
32277                 tag : 'div',
32278                 cls : 'btn-group roo-upload-cropbox-download',
32279                 action : 'download',
32280                 cn : [
32281                     {
32282                         tag : 'button',
32283                         cls : 'btn btn-default',
32284                         html : '<i class="fa fa-download"></i>'
32285                     }
32286                 ]
32287             },
32288             {
32289                 tag : 'div',
32290                 cls : 'btn-group roo-upload-cropbox-crop',
32291                 action : 'crop',
32292                 cn : [
32293                     {
32294                         tag : 'button',
32295                         cls : 'btn btn-default',
32296                         html : '<i class="fa fa-crop"></i>'
32297                     }
32298                 ]
32299             },
32300             {
32301                 tag : 'div',
32302                 cls : 'btn-group roo-upload-cropbox-trash',
32303                 action : 'trash',
32304                 cn : [
32305                     {
32306                         tag : 'button',
32307                         cls : 'btn btn-default',
32308                         html : '<i class="fa fa-trash"></i>'
32309                     }
32310                 ]
32311             },
32312             {
32313                 tag : 'div',
32314                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32315                 action : 'rotate-right',
32316                 cn : [
32317                     {
32318                         tag : 'button',
32319                         cls : 'btn btn-default',
32320                         html : '<i class="fa fa-repeat"></i>'
32321                     }
32322                 ]
32323             }
32324         ],
32325         ROTATOR : [
32326             {
32327                 tag : 'div',
32328                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32329                 action : 'rotate-left',
32330                 cn : [
32331                     {
32332                         tag : 'button',
32333                         cls : 'btn btn-default',
32334                         html : '<i class="fa fa-undo"></i>'
32335                     }
32336                 ]
32337             },
32338             {
32339                 tag : 'div',
32340                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32341                 action : 'rotate-right',
32342                 cn : [
32343                     {
32344                         tag : 'button',
32345                         cls : 'btn btn-default',
32346                         html : '<i class="fa fa-repeat"></i>'
32347                     }
32348                 ]
32349             }
32350         ]
32351     }
32352 });
32353
32354 /*
32355 * Licence: LGPL
32356 */
32357
32358 /**
32359  * @class Roo.bootstrap.DocumentManager
32360  * @extends Roo.bootstrap.Component
32361  * Bootstrap DocumentManager class
32362  * @cfg {String} paramName default 'imageUpload'
32363  * @cfg {String} toolTipName default 'filename'
32364  * @cfg {String} method default POST
32365  * @cfg {String} url action url
32366  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32367  * @cfg {Boolean} multiple multiple upload default true
32368  * @cfg {Number} thumbSize default 300
32369  * @cfg {String} fieldLabel
32370  * @cfg {Number} labelWidth default 4
32371  * @cfg {String} labelAlign (left|top) default left
32372  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32373 * @cfg {Number} labellg set the width of label (1-12)
32374  * @cfg {Number} labelmd set the width of label (1-12)
32375  * @cfg {Number} labelsm set the width of label (1-12)
32376  * @cfg {Number} labelxs set the width of label (1-12)
32377  * 
32378  * @constructor
32379  * Create a new DocumentManager
32380  * @param {Object} config The config object
32381  */
32382
32383 Roo.bootstrap.DocumentManager = function(config){
32384     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32385     
32386     this.files = [];
32387     this.delegates = [];
32388     
32389     this.addEvents({
32390         /**
32391          * @event initial
32392          * Fire when initial the DocumentManager
32393          * @param {Roo.bootstrap.DocumentManager} this
32394          */
32395         "initial" : true,
32396         /**
32397          * @event inspect
32398          * inspect selected file
32399          * @param {Roo.bootstrap.DocumentManager} this
32400          * @param {File} file
32401          */
32402         "inspect" : true,
32403         /**
32404          * @event exception
32405          * Fire when xhr load exception
32406          * @param {Roo.bootstrap.DocumentManager} this
32407          * @param {XMLHttpRequest} xhr
32408          */
32409         "exception" : true,
32410         /**
32411          * @event afterupload
32412          * Fire when xhr load exception
32413          * @param {Roo.bootstrap.DocumentManager} this
32414          * @param {XMLHttpRequest} xhr
32415          */
32416         "afterupload" : true,
32417         /**
32418          * @event prepare
32419          * prepare the form data
32420          * @param {Roo.bootstrap.DocumentManager} this
32421          * @param {Object} formData
32422          */
32423         "prepare" : true,
32424         /**
32425          * @event remove
32426          * Fire when remove the file
32427          * @param {Roo.bootstrap.DocumentManager} this
32428          * @param {Object} file
32429          */
32430         "remove" : true,
32431         /**
32432          * @event refresh
32433          * Fire after refresh the file
32434          * @param {Roo.bootstrap.DocumentManager} this
32435          */
32436         "refresh" : true,
32437         /**
32438          * @event click
32439          * Fire after click the image
32440          * @param {Roo.bootstrap.DocumentManager} this
32441          * @param {Object} file
32442          */
32443         "click" : true,
32444         /**
32445          * @event edit
32446          * Fire when upload a image and editable set to true
32447          * @param {Roo.bootstrap.DocumentManager} this
32448          * @param {Object} file
32449          */
32450         "edit" : true,
32451         /**
32452          * @event beforeselectfile
32453          * Fire before select file
32454          * @param {Roo.bootstrap.DocumentManager} this
32455          */
32456         "beforeselectfile" : true,
32457         /**
32458          * @event process
32459          * Fire before process file
32460          * @param {Roo.bootstrap.DocumentManager} this
32461          * @param {Object} file
32462          */
32463         "process" : true,
32464         /**
32465          * @event previewrendered
32466          * Fire when preview rendered
32467          * @param {Roo.bootstrap.DocumentManager} this
32468          * @param {Object} file
32469          */
32470         "previewrendered" : true,
32471         /**
32472          */
32473         "previewResize" : true
32474         
32475     });
32476 };
32477
32478 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32479     
32480     boxes : 0,
32481     inputName : '',
32482     thumbSize : 300,
32483     multiple : true,
32484     files : false,
32485     method : 'POST',
32486     url : '',
32487     paramName : 'imageUpload',
32488     toolTipName : 'filename',
32489     fieldLabel : '',
32490     labelWidth : 4,
32491     labelAlign : 'left',
32492     editable : true,
32493     delegates : false,
32494     xhr : false, 
32495     
32496     labellg : 0,
32497     labelmd : 0,
32498     labelsm : 0,
32499     labelxs : 0,
32500     
32501     getAutoCreate : function()
32502     {   
32503         var managerWidget = {
32504             tag : 'div',
32505             cls : 'roo-document-manager',
32506             cn : [
32507                 {
32508                     tag : 'input',
32509                     cls : 'roo-document-manager-selector',
32510                     type : 'file'
32511                 },
32512                 {
32513                     tag : 'div',
32514                     cls : 'roo-document-manager-uploader',
32515                     cn : [
32516                         {
32517                             tag : 'div',
32518                             cls : 'roo-document-manager-upload-btn',
32519                             html : '<i class="fa fa-plus"></i>'
32520                         }
32521                     ]
32522                     
32523                 }
32524             ]
32525         };
32526         
32527         var content = [
32528             {
32529                 tag : 'div',
32530                 cls : 'column col-md-12',
32531                 cn : managerWidget
32532             }
32533         ];
32534         
32535         if(this.fieldLabel.length){
32536             
32537             content = [
32538                 {
32539                     tag : 'div',
32540                     cls : 'column col-md-12',
32541                     html : this.fieldLabel
32542                 },
32543                 {
32544                     tag : 'div',
32545                     cls : 'column col-md-12',
32546                     cn : managerWidget
32547                 }
32548             ];
32549
32550             if(this.labelAlign == 'left'){
32551                 content = [
32552                     {
32553                         tag : 'div',
32554                         cls : 'column',
32555                         html : this.fieldLabel
32556                     },
32557                     {
32558                         tag : 'div',
32559                         cls : 'column',
32560                         cn : managerWidget
32561                     }
32562                 ];
32563                 
32564                 if(this.labelWidth > 12){
32565                     content[0].style = "width: " + this.labelWidth + 'px';
32566                 }
32567
32568                 if(this.labelWidth < 13 && this.labelmd == 0){
32569                     this.labelmd = this.labelWidth;
32570                 }
32571
32572                 if(this.labellg > 0){
32573                     content[0].cls += ' col-lg-' + this.labellg;
32574                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32575                 }
32576
32577                 if(this.labelmd > 0){
32578                     content[0].cls += ' col-md-' + this.labelmd;
32579                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32580                 }
32581
32582                 if(this.labelsm > 0){
32583                     content[0].cls += ' col-sm-' + this.labelsm;
32584                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32585                 }
32586
32587                 if(this.labelxs > 0){
32588                     content[0].cls += ' col-xs-' + this.labelxs;
32589                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32590                 }
32591                 
32592             }
32593         }
32594         
32595         var cfg = {
32596             tag : 'div',
32597             cls : 'row clearfix',
32598             cn : content
32599         };
32600         
32601         return cfg;
32602         
32603     },
32604     
32605     initEvents : function()
32606     {
32607         this.managerEl = this.el.select('.roo-document-manager', true).first();
32608         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32609         
32610         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32611         this.selectorEl.hide();
32612         
32613         if(this.multiple){
32614             this.selectorEl.attr('multiple', 'multiple');
32615         }
32616         
32617         this.selectorEl.on('change', this.onFileSelected, this);
32618         
32619         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32620         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32621         
32622         this.uploader.on('click', this.onUploaderClick, this);
32623         
32624         this.renderProgressDialog();
32625         
32626         var _this = this;
32627         
32628         window.addEventListener("resize", function() { _this.refresh(); } );
32629         
32630         this.fireEvent('initial', this);
32631     },
32632     
32633     renderProgressDialog : function()
32634     {
32635         var _this = this;
32636         
32637         this.progressDialog = new Roo.bootstrap.Modal({
32638             cls : 'roo-document-manager-progress-dialog',
32639             allow_close : false,
32640             animate : false,
32641             title : '',
32642             buttons : [
32643                 {
32644                     name  :'cancel',
32645                     weight : 'danger',
32646                     html : 'Cancel'
32647                 }
32648             ], 
32649             listeners : { 
32650                 btnclick : function() {
32651                     _this.uploadCancel();
32652                     this.hide();
32653                 }
32654             }
32655         });
32656          
32657         this.progressDialog.render(Roo.get(document.body));
32658          
32659         this.progress = new Roo.bootstrap.Progress({
32660             cls : 'roo-document-manager-progress',
32661             active : true,
32662             striped : true
32663         });
32664         
32665         this.progress.render(this.progressDialog.getChildContainer());
32666         
32667         this.progressBar = new Roo.bootstrap.ProgressBar({
32668             cls : 'roo-document-manager-progress-bar',
32669             aria_valuenow : 0,
32670             aria_valuemin : 0,
32671             aria_valuemax : 12,
32672             panel : 'success'
32673         });
32674         
32675         this.progressBar.render(this.progress.getChildContainer());
32676     },
32677     
32678     onUploaderClick : function(e)
32679     {
32680         e.preventDefault();
32681      
32682         if(this.fireEvent('beforeselectfile', this) != false){
32683             this.selectorEl.dom.click();
32684         }
32685         
32686     },
32687     
32688     onFileSelected : function(e)
32689     {
32690         e.preventDefault();
32691         
32692         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32693             return;
32694         }
32695         
32696         Roo.each(this.selectorEl.dom.files, function(file){
32697             if(this.fireEvent('inspect', this, file) != false){
32698                 this.files.push(file);
32699             }
32700         }, this);
32701         
32702         this.queue();
32703         
32704     },
32705     
32706     queue : function()
32707     {
32708         this.selectorEl.dom.value = '';
32709         
32710         if(!this.files || !this.files.length){
32711             return;
32712         }
32713         
32714         if(this.boxes > 0 && this.files.length > this.boxes){
32715             this.files = this.files.slice(0, this.boxes);
32716         }
32717         
32718         this.uploader.show();
32719         
32720         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32721             this.uploader.hide();
32722         }
32723         
32724         var _this = this;
32725         
32726         var files = [];
32727         
32728         var docs = [];
32729         
32730         Roo.each(this.files, function(file){
32731             
32732             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32733                 var f = this.renderPreview(file);
32734                 files.push(f);
32735                 return;
32736             }
32737             
32738             if(file.type.indexOf('image') != -1){
32739                 this.delegates.push(
32740                     (function(){
32741                         _this.process(file);
32742                     }).createDelegate(this)
32743                 );
32744         
32745                 return;
32746             }
32747             
32748             docs.push(
32749                 (function(){
32750                     _this.process(file);
32751                 }).createDelegate(this)
32752             );
32753             
32754         }, this);
32755         
32756         this.files = files;
32757         
32758         this.delegates = this.delegates.concat(docs);
32759         
32760         if(!this.delegates.length){
32761             this.refresh();
32762             return;
32763         }
32764         
32765         this.progressBar.aria_valuemax = this.delegates.length;
32766         
32767         this.arrange();
32768         
32769         return;
32770     },
32771     
32772     arrange : function()
32773     {
32774         if(!this.delegates.length){
32775             this.progressDialog.hide();
32776             this.refresh();
32777             return;
32778         }
32779         
32780         var delegate = this.delegates.shift();
32781         
32782         this.progressDialog.show();
32783         
32784         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32785         
32786         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32787         
32788         delegate();
32789     },
32790     
32791     refresh : function()
32792     {
32793         this.uploader.show();
32794         
32795         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32796             this.uploader.hide();
32797         }
32798         
32799         Roo.isTouch ? this.closable(false) : this.closable(true);
32800         
32801         this.fireEvent('refresh', this);
32802     },
32803     
32804     onRemove : function(e, el, o)
32805     {
32806         e.preventDefault();
32807         
32808         this.fireEvent('remove', this, o);
32809         
32810     },
32811     
32812     remove : function(o)
32813     {
32814         var files = [];
32815         
32816         Roo.each(this.files, function(file){
32817             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32818                 files.push(file);
32819                 return;
32820             }
32821
32822             o.target.remove();
32823
32824         }, this);
32825         
32826         this.files = files;
32827         
32828         this.refresh();
32829     },
32830     
32831     clear : function()
32832     {
32833         Roo.each(this.files, function(file){
32834             if(!file.target){
32835                 return;
32836             }
32837             
32838             file.target.remove();
32839
32840         }, this);
32841         
32842         this.files = [];
32843         
32844         this.refresh();
32845     },
32846     
32847     onClick : function(e, el, o)
32848     {
32849         e.preventDefault();
32850         
32851         this.fireEvent('click', this, o);
32852         
32853     },
32854     
32855     closable : function(closable)
32856     {
32857         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32858             
32859             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32860             
32861             if(closable){
32862                 el.show();
32863                 return;
32864             }
32865             
32866             el.hide();
32867             
32868         }, this);
32869     },
32870     
32871     xhrOnLoad : function(xhr)
32872     {
32873         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32874             el.remove();
32875         }, this);
32876         
32877         if (xhr.readyState !== 4) {
32878             this.arrange();
32879             this.fireEvent('exception', this, xhr);
32880             return;
32881         }
32882
32883         var response = Roo.decode(xhr.responseText);
32884         
32885         if(!response.success){
32886             this.arrange();
32887             this.fireEvent('exception', this, xhr);
32888             return;
32889         }
32890         
32891         var file = this.renderPreview(response.data);
32892         
32893         this.files.push(file);
32894         
32895         this.arrange();
32896         
32897         this.fireEvent('afterupload', this, xhr);
32898         
32899     },
32900     
32901     xhrOnError : function(xhr)
32902     {
32903         Roo.log('xhr on error');
32904         
32905         var response = Roo.decode(xhr.responseText);
32906           
32907         Roo.log(response);
32908         
32909         this.arrange();
32910     },
32911     
32912     process : function(file)
32913     {
32914         if(this.fireEvent('process', this, file) !== false){
32915             if(this.editable && file.type.indexOf('image') != -1){
32916                 this.fireEvent('edit', this, file);
32917                 return;
32918             }
32919
32920             this.uploadStart(file, false);
32921
32922             return;
32923         }
32924         
32925     },
32926     
32927     uploadStart : function(file, crop)
32928     {
32929         this.xhr = new XMLHttpRequest();
32930         
32931         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32932             this.arrange();
32933             return;
32934         }
32935         
32936         file.xhr = this.xhr;
32937             
32938         this.managerEl.createChild({
32939             tag : 'div',
32940             cls : 'roo-document-manager-loading',
32941             cn : [
32942                 {
32943                     tag : 'div',
32944                     tooltip : file.name,
32945                     cls : 'roo-document-manager-thumb',
32946                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32947                 }
32948             ]
32949
32950         });
32951
32952         this.xhr.open(this.method, this.url, true);
32953         
32954         var headers = {
32955             "Accept": "application/json",
32956             "Cache-Control": "no-cache",
32957             "X-Requested-With": "XMLHttpRequest"
32958         };
32959         
32960         for (var headerName in headers) {
32961             var headerValue = headers[headerName];
32962             if (headerValue) {
32963                 this.xhr.setRequestHeader(headerName, headerValue);
32964             }
32965         }
32966         
32967         var _this = this;
32968         
32969         this.xhr.onload = function()
32970         {
32971             _this.xhrOnLoad(_this.xhr);
32972         }
32973         
32974         this.xhr.onerror = function()
32975         {
32976             _this.xhrOnError(_this.xhr);
32977         }
32978         
32979         var formData = new FormData();
32980
32981         formData.append('returnHTML', 'NO');
32982         
32983         if(crop){
32984             formData.append('crop', crop);
32985         }
32986         
32987         formData.append(this.paramName, file, file.name);
32988         
32989         var options = {
32990             file : file, 
32991             manually : false
32992         };
32993         
32994         if(this.fireEvent('prepare', this, formData, options) != false){
32995             
32996             if(options.manually){
32997                 return;
32998             }
32999             
33000             this.xhr.send(formData);
33001             return;
33002         };
33003         
33004         this.uploadCancel();
33005     },
33006     
33007     uploadCancel : function()
33008     {
33009         if (this.xhr) {
33010             this.xhr.abort();
33011         }
33012         
33013         this.delegates = [];
33014         
33015         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33016             el.remove();
33017         }, this);
33018         
33019         this.arrange();
33020     },
33021     
33022     renderPreview : function(file)
33023     {
33024         if(typeof(file.target) != 'undefined' && file.target){
33025             return file;
33026         }
33027         
33028         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33029         
33030         var previewEl = this.managerEl.createChild({
33031             tag : 'div',
33032             cls : 'roo-document-manager-preview',
33033             cn : [
33034                 {
33035                     tag : 'div',
33036                     tooltip : file[this.toolTipName],
33037                     cls : 'roo-document-manager-thumb',
33038                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33039                 },
33040                 {
33041                     tag : 'button',
33042                     cls : 'close',
33043                     html : '<i class="fa fa-times-circle"></i>'
33044                 }
33045             ]
33046         });
33047
33048         var close = previewEl.select('button.close', true).first();
33049
33050         close.on('click', this.onRemove, this, file);
33051
33052         file.target = previewEl;
33053
33054         var image = previewEl.select('img', true).first();
33055         
33056         var _this = this;
33057         
33058         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33059         
33060         image.on('click', this.onClick, this, file);
33061         
33062         this.fireEvent('previewrendered', this, file);
33063         
33064         return file;
33065         
33066     },
33067     
33068     onPreviewLoad : function(file, image)
33069     {
33070         if(typeof(file.target) == 'undefined' || !file.target){
33071             return;
33072         }
33073         
33074         var width = image.dom.naturalWidth || image.dom.width;
33075         var height = image.dom.naturalHeight || image.dom.height;
33076         
33077         if(!this.previewResize) {
33078             return;
33079         }
33080         
33081         if(width > height){
33082             file.target.addClass('wide');
33083             return;
33084         }
33085         
33086         file.target.addClass('tall');
33087         return;
33088         
33089     },
33090     
33091     uploadFromSource : function(file, crop)
33092     {
33093         this.xhr = new XMLHttpRequest();
33094         
33095         this.managerEl.createChild({
33096             tag : 'div',
33097             cls : 'roo-document-manager-loading',
33098             cn : [
33099                 {
33100                     tag : 'div',
33101                     tooltip : file.name,
33102                     cls : 'roo-document-manager-thumb',
33103                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33104                 }
33105             ]
33106
33107         });
33108
33109         this.xhr.open(this.method, this.url, true);
33110         
33111         var headers = {
33112             "Accept": "application/json",
33113             "Cache-Control": "no-cache",
33114             "X-Requested-With": "XMLHttpRequest"
33115         };
33116         
33117         for (var headerName in headers) {
33118             var headerValue = headers[headerName];
33119             if (headerValue) {
33120                 this.xhr.setRequestHeader(headerName, headerValue);
33121             }
33122         }
33123         
33124         var _this = this;
33125         
33126         this.xhr.onload = function()
33127         {
33128             _this.xhrOnLoad(_this.xhr);
33129         }
33130         
33131         this.xhr.onerror = function()
33132         {
33133             _this.xhrOnError(_this.xhr);
33134         }
33135         
33136         var formData = new FormData();
33137
33138         formData.append('returnHTML', 'NO');
33139         
33140         formData.append('crop', crop);
33141         
33142         if(typeof(file.filename) != 'undefined'){
33143             formData.append('filename', file.filename);
33144         }
33145         
33146         if(typeof(file.mimetype) != 'undefined'){
33147             formData.append('mimetype', file.mimetype);
33148         }
33149         
33150         Roo.log(formData);
33151         
33152         if(this.fireEvent('prepare', this, formData) != false){
33153             this.xhr.send(formData);
33154         };
33155     }
33156 });
33157
33158 /*
33159 * Licence: LGPL
33160 */
33161
33162 /**
33163  * @class Roo.bootstrap.DocumentViewer
33164  * @extends Roo.bootstrap.Component
33165  * Bootstrap DocumentViewer class
33166  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33167  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33168  * 
33169  * @constructor
33170  * Create a new DocumentViewer
33171  * @param {Object} config The config object
33172  */
33173
33174 Roo.bootstrap.DocumentViewer = function(config){
33175     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33176     
33177     this.addEvents({
33178         /**
33179          * @event initial
33180          * Fire after initEvent
33181          * @param {Roo.bootstrap.DocumentViewer} this
33182          */
33183         "initial" : true,
33184         /**
33185          * @event click
33186          * Fire after click
33187          * @param {Roo.bootstrap.DocumentViewer} this
33188          */
33189         "click" : true,
33190         /**
33191          * @event download
33192          * Fire after download button
33193          * @param {Roo.bootstrap.DocumentViewer} this
33194          */
33195         "download" : true,
33196         /**
33197          * @event trash
33198          * Fire after trash button
33199          * @param {Roo.bootstrap.DocumentViewer} this
33200          */
33201         "trash" : true
33202         
33203     });
33204 };
33205
33206 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33207     
33208     showDownload : true,
33209     
33210     showTrash : true,
33211     
33212     getAutoCreate : function()
33213     {
33214         var cfg = {
33215             tag : 'div',
33216             cls : 'roo-document-viewer',
33217             cn : [
33218                 {
33219                     tag : 'div',
33220                     cls : 'roo-document-viewer-body',
33221                     cn : [
33222                         {
33223                             tag : 'div',
33224                             cls : 'roo-document-viewer-thumb',
33225                             cn : [
33226                                 {
33227                                     tag : 'img',
33228                                     cls : 'roo-document-viewer-image'
33229                                 }
33230                             ]
33231                         }
33232                     ]
33233                 },
33234                 {
33235                     tag : 'div',
33236                     cls : 'roo-document-viewer-footer',
33237                     cn : {
33238                         tag : 'div',
33239                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33240                         cn : [
33241                             {
33242                                 tag : 'div',
33243                                 cls : 'btn-group roo-document-viewer-download',
33244                                 cn : [
33245                                     {
33246                                         tag : 'button',
33247                                         cls : 'btn btn-default',
33248                                         html : '<i class="fa fa-download"></i>'
33249                                     }
33250                                 ]
33251                             },
33252                             {
33253                                 tag : 'div',
33254                                 cls : 'btn-group roo-document-viewer-trash',
33255                                 cn : [
33256                                     {
33257                                         tag : 'button',
33258                                         cls : 'btn btn-default',
33259                                         html : '<i class="fa fa-trash"></i>'
33260                                     }
33261                                 ]
33262                             }
33263                         ]
33264                     }
33265                 }
33266             ]
33267         };
33268         
33269         return cfg;
33270     },
33271     
33272     initEvents : function()
33273     {
33274         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33275         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33276         
33277         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33278         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33279         
33280         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33281         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33282         
33283         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33284         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33285         
33286         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33287         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33288         
33289         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33290         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33291         
33292         this.bodyEl.on('click', this.onClick, this);
33293         this.downloadBtn.on('click', this.onDownload, this);
33294         this.trashBtn.on('click', this.onTrash, this);
33295         
33296         this.downloadBtn.hide();
33297         this.trashBtn.hide();
33298         
33299         if(this.showDownload){
33300             this.downloadBtn.show();
33301         }
33302         
33303         if(this.showTrash){
33304             this.trashBtn.show();
33305         }
33306         
33307         if(!this.showDownload && !this.showTrash) {
33308             this.footerEl.hide();
33309         }
33310         
33311     },
33312     
33313     initial : function()
33314     {
33315         this.fireEvent('initial', this);
33316         
33317     },
33318     
33319     onClick : function(e)
33320     {
33321         e.preventDefault();
33322         
33323         this.fireEvent('click', this);
33324     },
33325     
33326     onDownload : function(e)
33327     {
33328         e.preventDefault();
33329         
33330         this.fireEvent('download', this);
33331     },
33332     
33333     onTrash : function(e)
33334     {
33335         e.preventDefault();
33336         
33337         this.fireEvent('trash', this);
33338     }
33339     
33340 });
33341 /*
33342  * - LGPL
33343  *
33344  * FieldLabel
33345  * 
33346  */
33347
33348 /**
33349  * @class Roo.bootstrap.form.FieldLabel
33350  * @extends Roo.bootstrap.Component
33351  * Bootstrap FieldLabel class
33352  * @cfg {String} html contents of the element
33353  * @cfg {String} tag tag of the element default label
33354  * @cfg {String} cls class of the element
33355  * @cfg {String} target label target 
33356  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33357  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33358  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33359  * @cfg {String} iconTooltip default "This field is required"
33360  * @cfg {String} indicatorpos (left|right) default left
33361  * 
33362  * @constructor
33363  * Create a new FieldLabel
33364  * @param {Object} config The config object
33365  */
33366
33367 Roo.bootstrap.form.FieldLabel = function(config){
33368     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33369     
33370     this.addEvents({
33371             /**
33372              * @event invalid
33373              * Fires after the field has been marked as invalid.
33374              * @param {Roo.form.FieldLabel} this
33375              * @param {String} msg The validation message
33376              */
33377             invalid : true,
33378             /**
33379              * @event valid
33380              * Fires after the field has been validated with no errors.
33381              * @param {Roo.form.FieldLabel} this
33382              */
33383             valid : true
33384         });
33385 };
33386
33387 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33388     
33389     tag: 'label',
33390     cls: '',
33391     html: '',
33392     target: '',
33393     allowBlank : true,
33394     invalidClass : 'has-warning',
33395     validClass : 'has-success',
33396     iconTooltip : 'This field is required',
33397     indicatorpos : 'left',
33398     
33399     getAutoCreate : function(){
33400         
33401         var cls = "";
33402         if (!this.allowBlank) {
33403             cls  = "visible";
33404         }
33405         
33406         var cfg = {
33407             tag : this.tag,
33408             cls : 'roo-bootstrap-field-label ' + this.cls,
33409             for : this.target,
33410             cn : [
33411                 {
33412                     tag : 'i',
33413                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33414                     tooltip : this.iconTooltip
33415                 },
33416                 {
33417                     tag : 'span',
33418                     html : this.html
33419                 }
33420             ] 
33421         };
33422         
33423         if(this.indicatorpos == 'right'){
33424             var cfg = {
33425                 tag : this.tag,
33426                 cls : 'roo-bootstrap-field-label ' + this.cls,
33427                 for : this.target,
33428                 cn : [
33429                     {
33430                         tag : 'span',
33431                         html : this.html
33432                     },
33433                     {
33434                         tag : 'i',
33435                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33436                         tooltip : this.iconTooltip
33437                     }
33438                 ] 
33439             };
33440         }
33441         
33442         return cfg;
33443     },
33444     
33445     initEvents: function() 
33446     {
33447         Roo.bootstrap.Element.superclass.initEvents.call(this);
33448         
33449         this.indicator = this.indicatorEl();
33450         
33451         if(this.indicator){
33452             this.indicator.removeClass('visible');
33453             this.indicator.addClass('invisible');
33454         }
33455         
33456         Roo.bootstrap.form.FieldLabel.register(this);
33457     },
33458     
33459     indicatorEl : function()
33460     {
33461         var indicator = this.el.select('i.roo-required-indicator',true).first();
33462         
33463         if(!indicator){
33464             return false;
33465         }
33466         
33467         return indicator;
33468         
33469     },
33470     
33471     /**
33472      * Mark this field as valid
33473      */
33474     markValid : function()
33475     {
33476         if(this.indicator){
33477             this.indicator.removeClass('visible');
33478             this.indicator.addClass('invisible');
33479         }
33480         if (Roo.bootstrap.version == 3) {
33481             this.el.removeClass(this.invalidClass);
33482             this.el.addClass(this.validClass);
33483         } else {
33484             this.el.removeClass('is-invalid');
33485             this.el.addClass('is-valid');
33486         }
33487         
33488         
33489         this.fireEvent('valid', this);
33490     },
33491     
33492     /**
33493      * Mark this field as invalid
33494      * @param {String} msg The validation message
33495      */
33496     markInvalid : function(msg)
33497     {
33498         if(this.indicator){
33499             this.indicator.removeClass('invisible');
33500             this.indicator.addClass('visible');
33501         }
33502           if (Roo.bootstrap.version == 3) {
33503             this.el.removeClass(this.validClass);
33504             this.el.addClass(this.invalidClass);
33505         } else {
33506             this.el.removeClass('is-valid');
33507             this.el.addClass('is-invalid');
33508         }
33509         
33510         
33511         this.fireEvent('invalid', this, msg);
33512     }
33513     
33514    
33515 });
33516
33517 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33518     
33519     groups: {},
33520     
33521      /**
33522     * register a FieldLabel Group
33523     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33524     */
33525     register : function(label)
33526     {
33527         if(this.groups.hasOwnProperty(label.target)){
33528             return;
33529         }
33530      
33531         this.groups[label.target] = label;
33532         
33533     },
33534     /**
33535     * fetch a FieldLabel Group based on the target
33536     * @param {string} target
33537     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33538     */
33539     get: function(target) {
33540         if (typeof(this.groups[target]) == 'undefined') {
33541             return false;
33542         }
33543         
33544         return this.groups[target] ;
33545     }
33546 });
33547
33548  
33549
33550  /*
33551  * - LGPL
33552  *
33553  * page DateSplitField.
33554  * 
33555  */
33556
33557
33558 /**
33559  * @class Roo.bootstrap.form.DateSplitField
33560  * @extends Roo.bootstrap.Component
33561  * Bootstrap DateSplitField class
33562  * @cfg {string} fieldLabel - the label associated
33563  * @cfg {Number} labelWidth set the width of label (0-12)
33564  * @cfg {String} labelAlign (top|left)
33565  * @cfg {Boolean} dayAllowBlank (true|false) default false
33566  * @cfg {Boolean} monthAllowBlank (true|false) default false
33567  * @cfg {Boolean} yearAllowBlank (true|false) default false
33568  * @cfg {string} dayPlaceholder 
33569  * @cfg {string} monthPlaceholder
33570  * @cfg {string} yearPlaceholder
33571  * @cfg {string} dayFormat default 'd'
33572  * @cfg {string} monthFormat default 'm'
33573  * @cfg {string} yearFormat default 'Y'
33574  * @cfg {Number} labellg set the width of label (1-12)
33575  * @cfg {Number} labelmd set the width of label (1-12)
33576  * @cfg {Number} labelsm set the width of label (1-12)
33577  * @cfg {Number} labelxs set the width of label (1-12)
33578
33579  *     
33580  * @constructor
33581  * Create a new DateSplitField
33582  * @param {Object} config The config object
33583  */
33584
33585 Roo.bootstrap.form.DateSplitField = function(config){
33586     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33587     
33588     this.addEvents({
33589         // raw events
33590          /**
33591          * @event years
33592          * getting the data of years
33593          * @param {Roo.bootstrap.form.DateSplitField} this
33594          * @param {Object} years
33595          */
33596         "years" : true,
33597         /**
33598          * @event days
33599          * getting the data of days
33600          * @param {Roo.bootstrap.form.DateSplitField} this
33601          * @param {Object} days
33602          */
33603         "days" : true,
33604         /**
33605          * @event invalid
33606          * Fires after the field has been marked as invalid.
33607          * @param {Roo.form.Field} this
33608          * @param {String} msg The validation message
33609          */
33610         invalid : true,
33611        /**
33612          * @event valid
33613          * Fires after the field has been validated with no errors.
33614          * @param {Roo.form.Field} this
33615          */
33616         valid : true
33617     });
33618 };
33619
33620 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33621     
33622     fieldLabel : '',
33623     labelAlign : 'top',
33624     labelWidth : 3,
33625     dayAllowBlank : false,
33626     monthAllowBlank : false,
33627     yearAllowBlank : false,
33628     dayPlaceholder : '',
33629     monthPlaceholder : '',
33630     yearPlaceholder : '',
33631     dayFormat : 'd',
33632     monthFormat : 'm',
33633     yearFormat : 'Y',
33634     isFormField : true,
33635     labellg : 0,
33636     labelmd : 0,
33637     labelsm : 0,
33638     labelxs : 0,
33639     
33640     getAutoCreate : function()
33641     {
33642         var cfg = {
33643             tag : 'div',
33644             cls : 'row roo-date-split-field-group',
33645             cn : [
33646                 {
33647                     tag : 'input',
33648                     type : 'hidden',
33649                     cls : 'form-hidden-field roo-date-split-field-group-value',
33650                     name : this.name
33651                 }
33652             ]
33653         };
33654         
33655         var labelCls = 'col-md-12';
33656         var contentCls = 'col-md-4';
33657         
33658         if(this.fieldLabel){
33659             
33660             var label = {
33661                 tag : 'div',
33662                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33663                 cn : [
33664                     {
33665                         tag : 'label',
33666                         html : this.fieldLabel
33667                     }
33668                 ]
33669             };
33670             
33671             if(this.labelAlign == 'left'){
33672             
33673                 if(this.labelWidth > 12){
33674                     label.style = "width: " + this.labelWidth + 'px';
33675                 }
33676
33677                 if(this.labelWidth < 13 && this.labelmd == 0){
33678                     this.labelmd = this.labelWidth;
33679                 }
33680
33681                 if(this.labellg > 0){
33682                     labelCls = ' col-lg-' + this.labellg;
33683                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33684                 }
33685
33686                 if(this.labelmd > 0){
33687                     labelCls = ' col-md-' + this.labelmd;
33688                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33689                 }
33690
33691                 if(this.labelsm > 0){
33692                     labelCls = ' col-sm-' + this.labelsm;
33693                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33694                 }
33695
33696                 if(this.labelxs > 0){
33697                     labelCls = ' col-xs-' + this.labelxs;
33698                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33699                 }
33700             }
33701             
33702             label.cls += ' ' + labelCls;
33703             
33704             cfg.cn.push(label);
33705         }
33706         
33707         Roo.each(['day', 'month', 'year'], function(t){
33708             cfg.cn.push({
33709                 tag : 'div',
33710                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33711             });
33712         }, this);
33713         
33714         return cfg;
33715     },
33716     
33717     inputEl: function ()
33718     {
33719         return this.el.select('.roo-date-split-field-group-value', true).first();
33720     },
33721     
33722     onRender : function(ct, position) 
33723     {
33724         var _this = this;
33725         
33726         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33727         
33728         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33729         
33730         this.dayField = new Roo.bootstrap.form.ComboBox({
33731             allowBlank : this.dayAllowBlank,
33732             alwaysQuery : true,
33733             displayField : 'value',
33734             editable : false,
33735             fieldLabel : '',
33736             forceSelection : true,
33737             mode : 'local',
33738             placeholder : this.dayPlaceholder,
33739             selectOnFocus : true,
33740             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33741             triggerAction : 'all',
33742             typeAhead : true,
33743             valueField : 'value',
33744             store : new Roo.data.SimpleStore({
33745                 data : (function() {    
33746                     var days = [];
33747                     _this.fireEvent('days', _this, days);
33748                     return days;
33749                 })(),
33750                 fields : [ 'value' ]
33751             }),
33752             listeners : {
33753                 select : function (_self, record, index)
33754                 {
33755                     _this.setValue(_this.getValue());
33756                 }
33757             }
33758         });
33759
33760         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33761         
33762         this.monthField = new Roo.bootstrap.form.MonthField({
33763             after : '<i class=\"fa fa-calendar\"></i>',
33764             allowBlank : this.monthAllowBlank,
33765             placeholder : this.monthPlaceholder,
33766             readOnly : true,
33767             listeners : {
33768                 render : function (_self)
33769                 {
33770                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33771                         e.preventDefault();
33772                         _self.focus();
33773                     });
33774                 },
33775                 select : function (_self, oldvalue, newvalue)
33776                 {
33777                     _this.setValue(_this.getValue());
33778                 }
33779             }
33780         });
33781         
33782         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33783         
33784         this.yearField = new Roo.bootstrap.form.ComboBox({
33785             allowBlank : this.yearAllowBlank,
33786             alwaysQuery : true,
33787             displayField : 'value',
33788             editable : false,
33789             fieldLabel : '',
33790             forceSelection : true,
33791             mode : 'local',
33792             placeholder : this.yearPlaceholder,
33793             selectOnFocus : true,
33794             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33795             triggerAction : 'all',
33796             typeAhead : true,
33797             valueField : 'value',
33798             store : new Roo.data.SimpleStore({
33799                 data : (function() {
33800                     var years = [];
33801                     _this.fireEvent('years', _this, years);
33802                     return years;
33803                 })(),
33804                 fields : [ 'value' ]
33805             }),
33806             listeners : {
33807                 select : function (_self, record, index)
33808                 {
33809                     _this.setValue(_this.getValue());
33810                 }
33811             }
33812         });
33813
33814         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33815     },
33816     
33817     setValue : function(v, format)
33818     {
33819         this.inputEl.dom.value = v;
33820         
33821         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33822         
33823         var d = Date.parseDate(v, f);
33824         
33825         if(!d){
33826             this.validate();
33827             return;
33828         }
33829         
33830         this.setDay(d.format(this.dayFormat));
33831         this.setMonth(d.format(this.monthFormat));
33832         this.setYear(d.format(this.yearFormat));
33833         
33834         this.validate();
33835         
33836         return;
33837     },
33838     
33839     setDay : function(v)
33840     {
33841         this.dayField.setValue(v);
33842         this.inputEl.dom.value = this.getValue();
33843         this.validate();
33844         return;
33845     },
33846     
33847     setMonth : function(v)
33848     {
33849         this.monthField.setValue(v, true);
33850         this.inputEl.dom.value = this.getValue();
33851         this.validate();
33852         return;
33853     },
33854     
33855     setYear : function(v)
33856     {
33857         this.yearField.setValue(v);
33858         this.inputEl.dom.value = this.getValue();
33859         this.validate();
33860         return;
33861     },
33862     
33863     getDay : function()
33864     {
33865         return this.dayField.getValue();
33866     },
33867     
33868     getMonth : function()
33869     {
33870         return this.monthField.getValue();
33871     },
33872     
33873     getYear : function()
33874     {
33875         return this.yearField.getValue();
33876     },
33877     
33878     getValue : function()
33879     {
33880         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33881         
33882         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33883         
33884         return date;
33885     },
33886     
33887     reset : function()
33888     {
33889         this.setDay('');
33890         this.setMonth('');
33891         this.setYear('');
33892         this.inputEl.dom.value = '';
33893         this.validate();
33894         return;
33895     },
33896     
33897     validate : function()
33898     {
33899         var d = this.dayField.validate();
33900         var m = this.monthField.validate();
33901         var y = this.yearField.validate();
33902         
33903         var valid = true;
33904         
33905         if(
33906                 (!this.dayAllowBlank && !d) ||
33907                 (!this.monthAllowBlank && !m) ||
33908                 (!this.yearAllowBlank && !y)
33909         ){
33910             valid = false;
33911         }
33912         
33913         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33914             return valid;
33915         }
33916         
33917         if(valid){
33918             this.markValid();
33919             return valid;
33920         }
33921         
33922         this.markInvalid();
33923         
33924         return valid;
33925     },
33926     
33927     markValid : function()
33928     {
33929         
33930         var label = this.el.select('label', true).first();
33931         var icon = this.el.select('i.fa-star', true).first();
33932
33933         if(label && icon){
33934             icon.remove();
33935         }
33936         
33937         this.fireEvent('valid', this);
33938     },
33939     
33940      /**
33941      * Mark this field as invalid
33942      * @param {String} msg The validation message
33943      */
33944     markInvalid : function(msg)
33945     {
33946         
33947         var label = this.el.select('label', true).first();
33948         var icon = this.el.select('i.fa-star', true).first();
33949
33950         if(label && !icon){
33951             this.el.select('.roo-date-split-field-label', true).createChild({
33952                 tag : 'i',
33953                 cls : 'text-danger fa fa-lg fa-star',
33954                 tooltip : 'This field is required',
33955                 style : 'margin-right:5px;'
33956             }, label, true);
33957         }
33958         
33959         this.fireEvent('invalid', this, msg);
33960     },
33961     
33962     clearInvalid : function()
33963     {
33964         var label = this.el.select('label', true).first();
33965         var icon = this.el.select('i.fa-star', true).first();
33966
33967         if(label && icon){
33968             icon.remove();
33969         }
33970         
33971         this.fireEvent('valid', this);
33972     },
33973     
33974     getName: function()
33975     {
33976         return this.name;
33977     }
33978     
33979 });
33980
33981  
33982
33983 /**
33984  * @class Roo.bootstrap.LayoutMasonry
33985  * @extends Roo.bootstrap.Component
33986  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
33987  * Bootstrap Layout Masonry class
33988  *
33989  * This is based on 
33990  * http://masonry.desandro.com
33991  *
33992  * The idea is to render all the bricks based on vertical width...
33993  *
33994  * The original code extends 'outlayer' - we might need to use that....
33995
33996  * @constructor
33997  * Create a new Element
33998  * @param {Object} config The config object
33999  */
34000
34001 Roo.bootstrap.LayoutMasonry = function(config){
34002     
34003     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34004     
34005     this.bricks = [];
34006     
34007     Roo.bootstrap.LayoutMasonry.register(this);
34008     
34009     this.addEvents({
34010         // raw events
34011         /**
34012          * @event layout
34013          * Fire after layout the items
34014          * @param {Roo.bootstrap.LayoutMasonry} this
34015          * @param {Roo.EventObject} e
34016          */
34017         "layout" : true
34018     });
34019     
34020 };
34021
34022 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34023     
34024     /**
34025      * @cfg {Boolean} isLayoutInstant = no animation?
34026      */   
34027     isLayoutInstant : false, // needed?
34028    
34029     /**
34030      * @cfg {Number} boxWidth  width of the columns
34031      */   
34032     boxWidth : 450,
34033     
34034       /**
34035      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34036      */   
34037     boxHeight : 0,
34038     
34039     /**
34040      * @cfg {Number} padWidth padding below box..
34041      */   
34042     padWidth : 10, 
34043     
34044     /**
34045      * @cfg {Number} gutter gutter width..
34046      */   
34047     gutter : 10,
34048     
34049      /**
34050      * @cfg {Number} maxCols maximum number of columns
34051      */   
34052     
34053     maxCols: 0,
34054     
34055     /**
34056      * @cfg {Boolean} isAutoInitial defalut true
34057      */   
34058     isAutoInitial : true, 
34059     
34060     containerWidth: 0,
34061     
34062     /**
34063      * @cfg {Boolean} isHorizontal defalut false
34064      */   
34065     isHorizontal : false, 
34066
34067     currentSize : null,
34068     
34069     tag: 'div',
34070     
34071     cls: '',
34072     
34073     bricks: null, //CompositeElement
34074     
34075     cols : 1,
34076     
34077     _isLayoutInited : false,
34078     
34079 //    isAlternative : false, // only use for vertical layout...
34080     
34081     /**
34082      * @cfg {Number} alternativePadWidth padding below box..
34083      */   
34084     alternativePadWidth : 50,
34085     
34086     selectedBrick : [],
34087     
34088     getAutoCreate : function(){
34089         
34090         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34091         
34092         var cfg = {
34093             tag: this.tag,
34094             cls: 'blog-masonary-wrapper ' + this.cls,
34095             cn : {
34096                 cls : 'mas-boxes masonary'
34097             }
34098         };
34099         
34100         return cfg;
34101     },
34102     
34103     getChildContainer: function( )
34104     {
34105         if (this.boxesEl) {
34106             return this.boxesEl;
34107         }
34108         
34109         this.boxesEl = this.el.select('.mas-boxes').first();
34110         
34111         return this.boxesEl;
34112     },
34113     
34114     
34115     initEvents : function()
34116     {
34117         var _this = this;
34118         
34119         if(this.isAutoInitial){
34120             Roo.log('hook children rendered');
34121             this.on('childrenrendered', function() {
34122                 Roo.log('children rendered');
34123                 _this.initial();
34124             } ,this);
34125         }
34126     },
34127     
34128     initial : function()
34129     {
34130         this.selectedBrick = [];
34131         
34132         this.currentSize = this.el.getBox(true);
34133         
34134         Roo.EventManager.onWindowResize(this.resize, this); 
34135
34136         if(!this.isAutoInitial){
34137             this.layout();
34138             return;
34139         }
34140         
34141         this.layout();
34142         
34143         return;
34144         //this.layout.defer(500,this);
34145         
34146     },
34147     
34148     resize : function()
34149     {
34150         var cs = this.el.getBox(true);
34151         
34152         if (
34153                 this.currentSize.width == cs.width && 
34154                 this.currentSize.x == cs.x && 
34155                 this.currentSize.height == cs.height && 
34156                 this.currentSize.y == cs.y 
34157         ) {
34158             Roo.log("no change in with or X or Y");
34159             return;
34160         }
34161         
34162         this.currentSize = cs;
34163         
34164         this.layout();
34165         
34166     },
34167     
34168     layout : function()
34169     {   
34170         this._resetLayout();
34171         
34172         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34173         
34174         this.layoutItems( isInstant );
34175       
34176         this._isLayoutInited = true;
34177         
34178         this.fireEvent('layout', this);
34179         
34180     },
34181     
34182     _resetLayout : function()
34183     {
34184         if(this.isHorizontal){
34185             this.horizontalMeasureColumns();
34186             return;
34187         }
34188         
34189         this.verticalMeasureColumns();
34190         
34191     },
34192     
34193     verticalMeasureColumns : function()
34194     {
34195         this.getContainerWidth();
34196         
34197 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34198 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34199 //            return;
34200 //        }
34201         
34202         var boxWidth = this.boxWidth + this.padWidth;
34203         
34204         if(this.containerWidth < this.boxWidth){
34205             boxWidth = this.containerWidth
34206         }
34207         
34208         var containerWidth = this.containerWidth;
34209         
34210         var cols = Math.floor(containerWidth / boxWidth);
34211         
34212         this.cols = Math.max( cols, 1 );
34213         
34214         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34215         
34216         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34217         
34218         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34219         
34220         this.colWidth = boxWidth + avail - this.padWidth;
34221         
34222         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34223         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34224     },
34225     
34226     horizontalMeasureColumns : function()
34227     {
34228         this.getContainerWidth();
34229         
34230         var boxWidth = this.boxWidth;
34231         
34232         if(this.containerWidth < boxWidth){
34233             boxWidth = this.containerWidth;
34234         }
34235         
34236         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34237         
34238         this.el.setHeight(boxWidth);
34239         
34240     },
34241     
34242     getContainerWidth : function()
34243     {
34244         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34245     },
34246     
34247     layoutItems : function( isInstant )
34248     {
34249         Roo.log(this.bricks);
34250         
34251         var items = Roo.apply([], this.bricks);
34252         
34253         if(this.isHorizontal){
34254             this._horizontalLayoutItems( items , isInstant );
34255             return;
34256         }
34257         
34258 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34259 //            this._verticalAlternativeLayoutItems( items , isInstant );
34260 //            return;
34261 //        }
34262         
34263         this._verticalLayoutItems( items , isInstant );
34264         
34265     },
34266     
34267     _verticalLayoutItems : function ( items , isInstant)
34268     {
34269         if ( !items || !items.length ) {
34270             return;
34271         }
34272         
34273         var standard = [
34274             ['xs', 'xs', 'xs', 'tall'],
34275             ['xs', 'xs', 'tall'],
34276             ['xs', 'xs', 'sm'],
34277             ['xs', 'xs', 'xs'],
34278             ['xs', 'tall'],
34279             ['xs', 'sm'],
34280             ['xs', 'xs'],
34281             ['xs'],
34282             
34283             ['sm', 'xs', 'xs'],
34284             ['sm', 'xs'],
34285             ['sm'],
34286             
34287             ['tall', 'xs', 'xs', 'xs'],
34288             ['tall', 'xs', 'xs'],
34289             ['tall', 'xs'],
34290             ['tall']
34291             
34292         ];
34293         
34294         var queue = [];
34295         
34296         var boxes = [];
34297         
34298         var box = [];
34299         
34300         Roo.each(items, function(item, k){
34301             
34302             switch (item.size) {
34303                 // these layouts take up a full box,
34304                 case 'md' :
34305                 case 'md-left' :
34306                 case 'md-right' :
34307                 case 'wide' :
34308                     
34309                     if(box.length){
34310                         boxes.push(box);
34311                         box = [];
34312                     }
34313                     
34314                     boxes.push([item]);
34315                     
34316                     break;
34317                     
34318                 case 'xs' :
34319                 case 'sm' :
34320                 case 'tall' :
34321                     
34322                     box.push(item);
34323                     
34324                     break;
34325                 default :
34326                     break;
34327                     
34328             }
34329             
34330         }, this);
34331         
34332         if(box.length){
34333             boxes.push(box);
34334             box = [];
34335         }
34336         
34337         var filterPattern = function(box, length)
34338         {
34339             if(!box.length){
34340                 return;
34341             }
34342             
34343             var match = false;
34344             
34345             var pattern = box.slice(0, length);
34346             
34347             var format = [];
34348             
34349             Roo.each(pattern, function(i){
34350                 format.push(i.size);
34351             }, this);
34352             
34353             Roo.each(standard, function(s){
34354                 
34355                 if(String(s) != String(format)){
34356                     return;
34357                 }
34358                 
34359                 match = true;
34360                 return false;
34361                 
34362             }, this);
34363             
34364             if(!match && length == 1){
34365                 return;
34366             }
34367             
34368             if(!match){
34369                 filterPattern(box, length - 1);
34370                 return;
34371             }
34372                 
34373             queue.push(pattern);
34374
34375             box = box.slice(length, box.length);
34376
34377             filterPattern(box, 4);
34378
34379             return;
34380             
34381         }
34382         
34383         Roo.each(boxes, function(box, k){
34384             
34385             if(!box.length){
34386                 return;
34387             }
34388             
34389             if(box.length == 1){
34390                 queue.push(box);
34391                 return;
34392             }
34393             
34394             filterPattern(box, 4);
34395             
34396         }, this);
34397         
34398         this._processVerticalLayoutQueue( queue, isInstant );
34399         
34400     },
34401     
34402 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34403 //    {
34404 //        if ( !items || !items.length ) {
34405 //            return;
34406 //        }
34407 //
34408 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34409 //        
34410 //    },
34411     
34412     _horizontalLayoutItems : function ( items , isInstant)
34413     {
34414         if ( !items || !items.length || items.length < 3) {
34415             return;
34416         }
34417         
34418         items.reverse();
34419         
34420         var eItems = items.slice(0, 3);
34421         
34422         items = items.slice(3, items.length);
34423         
34424         var standard = [
34425             ['xs', 'xs', 'xs', 'wide'],
34426             ['xs', 'xs', 'wide'],
34427             ['xs', 'xs', 'sm'],
34428             ['xs', 'xs', 'xs'],
34429             ['xs', 'wide'],
34430             ['xs', 'sm'],
34431             ['xs', 'xs'],
34432             ['xs'],
34433             
34434             ['sm', 'xs', 'xs'],
34435             ['sm', 'xs'],
34436             ['sm'],
34437             
34438             ['wide', 'xs', 'xs', 'xs'],
34439             ['wide', 'xs', 'xs'],
34440             ['wide', 'xs'],
34441             ['wide'],
34442             
34443             ['wide-thin']
34444         ];
34445         
34446         var queue = [];
34447         
34448         var boxes = [];
34449         
34450         var box = [];
34451         
34452         Roo.each(items, function(item, k){
34453             
34454             switch (item.size) {
34455                 case 'md' :
34456                 case 'md-left' :
34457                 case 'md-right' :
34458                 case 'tall' :
34459                     
34460                     if(box.length){
34461                         boxes.push(box);
34462                         box = [];
34463                     }
34464                     
34465                     boxes.push([item]);
34466                     
34467                     break;
34468                     
34469                 case 'xs' :
34470                 case 'sm' :
34471                 case 'wide' :
34472                 case 'wide-thin' :
34473                     
34474                     box.push(item);
34475                     
34476                     break;
34477                 default :
34478                     break;
34479                     
34480             }
34481             
34482         }, this);
34483         
34484         if(box.length){
34485             boxes.push(box);
34486             box = [];
34487         }
34488         
34489         var filterPattern = function(box, length)
34490         {
34491             if(!box.length){
34492                 return;
34493             }
34494             
34495             var match = false;
34496             
34497             var pattern = box.slice(0, length);
34498             
34499             var format = [];
34500             
34501             Roo.each(pattern, function(i){
34502                 format.push(i.size);
34503             }, this);
34504             
34505             Roo.each(standard, function(s){
34506                 
34507                 if(String(s) != String(format)){
34508                     return;
34509                 }
34510                 
34511                 match = true;
34512                 return false;
34513                 
34514             }, this);
34515             
34516             if(!match && length == 1){
34517                 return;
34518             }
34519             
34520             if(!match){
34521                 filterPattern(box, length - 1);
34522                 return;
34523             }
34524                 
34525             queue.push(pattern);
34526
34527             box = box.slice(length, box.length);
34528
34529             filterPattern(box, 4);
34530
34531             return;
34532             
34533         }
34534         
34535         Roo.each(boxes, function(box, k){
34536             
34537             if(!box.length){
34538                 return;
34539             }
34540             
34541             if(box.length == 1){
34542                 queue.push(box);
34543                 return;
34544             }
34545             
34546             filterPattern(box, 4);
34547             
34548         }, this);
34549         
34550         
34551         var prune = [];
34552         
34553         var pos = this.el.getBox(true);
34554         
34555         var minX = pos.x;
34556         
34557         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34558         
34559         var hit_end = false;
34560         
34561         Roo.each(queue, function(box){
34562             
34563             if(hit_end){
34564                 
34565                 Roo.each(box, function(b){
34566                 
34567                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34568                     b.el.hide();
34569
34570                 }, this);
34571
34572                 return;
34573             }
34574             
34575             var mx = 0;
34576             
34577             Roo.each(box, function(b){
34578                 
34579                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34580                 b.el.show();
34581
34582                 mx = Math.max(mx, b.x);
34583                 
34584             }, this);
34585             
34586             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34587             
34588             if(maxX < minX){
34589                 
34590                 Roo.each(box, function(b){
34591                 
34592                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34593                     b.el.hide();
34594                     
34595                 }, this);
34596                 
34597                 hit_end = true;
34598                 
34599                 return;
34600             }
34601             
34602             prune.push(box);
34603             
34604         }, this);
34605         
34606         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34607     },
34608     
34609     /** Sets position of item in DOM
34610     * @param {Element} item
34611     * @param {Number} x - horizontal position
34612     * @param {Number} y - vertical position
34613     * @param {Boolean} isInstant - disables transitions
34614     */
34615     _processVerticalLayoutQueue : function( queue, isInstant )
34616     {
34617         var pos = this.el.getBox(true);
34618         var x = pos.x;
34619         var y = pos.y;
34620         var maxY = [];
34621         
34622         for (var i = 0; i < this.cols; i++){
34623             maxY[i] = pos.y;
34624         }
34625         
34626         Roo.each(queue, function(box, k){
34627             
34628             var col = k % this.cols;
34629             
34630             Roo.each(box, function(b,kk){
34631                 
34632                 b.el.position('absolute');
34633                 
34634                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34635                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34636                 
34637                 if(b.size == 'md-left' || b.size == 'md-right'){
34638                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34639                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34640                 }
34641                 
34642                 b.el.setWidth(width);
34643                 b.el.setHeight(height);
34644                 // iframe?
34645                 b.el.select('iframe',true).setSize(width,height);
34646                 
34647             }, this);
34648             
34649             for (var i = 0; i < this.cols; i++){
34650                 
34651                 if(maxY[i] < maxY[col]){
34652                     col = i;
34653                     continue;
34654                 }
34655                 
34656                 col = Math.min(col, i);
34657                 
34658             }
34659             
34660             x = pos.x + col * (this.colWidth + this.padWidth);
34661             
34662             y = maxY[col];
34663             
34664             var positions = [];
34665             
34666             switch (box.length){
34667                 case 1 :
34668                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34669                     break;
34670                 case 2 :
34671                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34672                     break;
34673                 case 3 :
34674                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34675                     break;
34676                 case 4 :
34677                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34678                     break;
34679                 default :
34680                     break;
34681             }
34682             
34683             Roo.each(box, function(b,kk){
34684                 
34685                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34686                 
34687                 var sz = b.el.getSize();
34688                 
34689                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34690                 
34691             }, this);
34692             
34693         }, this);
34694         
34695         var mY = 0;
34696         
34697         for (var i = 0; i < this.cols; i++){
34698             mY = Math.max(mY, maxY[i]);
34699         }
34700         
34701         this.el.setHeight(mY - pos.y);
34702         
34703     },
34704     
34705 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34706 //    {
34707 //        var pos = this.el.getBox(true);
34708 //        var x = pos.x;
34709 //        var y = pos.y;
34710 //        var maxX = pos.right;
34711 //        
34712 //        var maxHeight = 0;
34713 //        
34714 //        Roo.each(items, function(item, k){
34715 //            
34716 //            var c = k % 2;
34717 //            
34718 //            item.el.position('absolute');
34719 //                
34720 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34721 //
34722 //            item.el.setWidth(width);
34723 //
34724 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34725 //
34726 //            item.el.setHeight(height);
34727 //            
34728 //            if(c == 0){
34729 //                item.el.setXY([x, y], isInstant ? false : true);
34730 //            } else {
34731 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34732 //            }
34733 //            
34734 //            y = y + height + this.alternativePadWidth;
34735 //            
34736 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34737 //            
34738 //        }, this);
34739 //        
34740 //        this.el.setHeight(maxHeight);
34741 //        
34742 //    },
34743     
34744     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34745     {
34746         var pos = this.el.getBox(true);
34747         
34748         var minX = pos.x;
34749         var minY = pos.y;
34750         
34751         var maxX = pos.right;
34752         
34753         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34754         
34755         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34756         
34757         Roo.each(queue, function(box, k){
34758             
34759             Roo.each(box, function(b, kk){
34760                 
34761                 b.el.position('absolute');
34762                 
34763                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34764                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34765                 
34766                 if(b.size == 'md-left' || b.size == 'md-right'){
34767                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34768                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34769                 }
34770                 
34771                 b.el.setWidth(width);
34772                 b.el.setHeight(height);
34773                 
34774             }, this);
34775             
34776             if(!box.length){
34777                 return;
34778             }
34779             
34780             var positions = [];
34781             
34782             switch (box.length){
34783                 case 1 :
34784                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34785                     break;
34786                 case 2 :
34787                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34788                     break;
34789                 case 3 :
34790                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34791                     break;
34792                 case 4 :
34793                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34794                     break;
34795                 default :
34796                     break;
34797             }
34798             
34799             Roo.each(box, function(b,kk){
34800                 
34801                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34802                 
34803                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34804                 
34805             }, this);
34806             
34807         }, this);
34808         
34809     },
34810     
34811     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34812     {
34813         Roo.each(eItems, function(b,k){
34814             
34815             b.size = (k == 0) ? 'sm' : 'xs';
34816             b.x = (k == 0) ? 2 : 1;
34817             b.y = (k == 0) ? 2 : 1;
34818             
34819             b.el.position('absolute');
34820             
34821             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34822                 
34823             b.el.setWidth(width);
34824             
34825             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34826             
34827             b.el.setHeight(height);
34828             
34829         }, this);
34830
34831         var positions = [];
34832         
34833         positions.push({
34834             x : maxX - this.unitWidth * 2 - this.gutter,
34835             y : minY
34836         });
34837         
34838         positions.push({
34839             x : maxX - this.unitWidth,
34840             y : minY + (this.unitWidth + this.gutter) * 2
34841         });
34842         
34843         positions.push({
34844             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34845             y : minY
34846         });
34847         
34848         Roo.each(eItems, function(b,k){
34849             
34850             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34851
34852         }, this);
34853         
34854     },
34855     
34856     getVerticalOneBoxColPositions : function(x, y, box)
34857     {
34858         var pos = [];
34859         
34860         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34861         
34862         if(box[0].size == 'md-left'){
34863             rand = 0;
34864         }
34865         
34866         if(box[0].size == 'md-right'){
34867             rand = 1;
34868         }
34869         
34870         pos.push({
34871             x : x + (this.unitWidth + this.gutter) * rand,
34872             y : y
34873         });
34874         
34875         return pos;
34876     },
34877     
34878     getVerticalTwoBoxColPositions : function(x, y, box)
34879     {
34880         var pos = [];
34881         
34882         if(box[0].size == 'xs'){
34883             
34884             pos.push({
34885                 x : x,
34886                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34887             });
34888
34889             pos.push({
34890                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34891                 y : y
34892             });
34893             
34894             return pos;
34895             
34896         }
34897         
34898         pos.push({
34899             x : x,
34900             y : y
34901         });
34902
34903         pos.push({
34904             x : x + (this.unitWidth + this.gutter) * 2,
34905             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34906         });
34907         
34908         return pos;
34909         
34910     },
34911     
34912     getVerticalThreeBoxColPositions : function(x, y, box)
34913     {
34914         var pos = [];
34915         
34916         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34917             
34918             pos.push({
34919                 x : x,
34920                 y : y
34921             });
34922
34923             pos.push({
34924                 x : x + (this.unitWidth + this.gutter) * 1,
34925                 y : y
34926             });
34927             
34928             pos.push({
34929                 x : x + (this.unitWidth + this.gutter) * 2,
34930                 y : y
34931             });
34932             
34933             return pos;
34934             
34935         }
34936         
34937         if(box[0].size == 'xs' && box[1].size == 'xs'){
34938             
34939             pos.push({
34940                 x : x,
34941                 y : y
34942             });
34943
34944             pos.push({
34945                 x : x,
34946                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34947             });
34948             
34949             pos.push({
34950                 x : x + (this.unitWidth + this.gutter) * 1,
34951                 y : y
34952             });
34953             
34954             return pos;
34955             
34956         }
34957         
34958         pos.push({
34959             x : x,
34960             y : y
34961         });
34962
34963         pos.push({
34964             x : x + (this.unitWidth + this.gutter) * 2,
34965             y : y
34966         });
34967
34968         pos.push({
34969             x : x + (this.unitWidth + this.gutter) * 2,
34970             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34971         });
34972             
34973         return pos;
34974         
34975     },
34976     
34977     getVerticalFourBoxColPositions : function(x, y, box)
34978     {
34979         var pos = [];
34980         
34981         if(box[0].size == 'xs'){
34982             
34983             pos.push({
34984                 x : x,
34985                 y : y
34986             });
34987
34988             pos.push({
34989                 x : x,
34990                 y : y + (this.unitHeight + this.gutter) * 1
34991             });
34992             
34993             pos.push({
34994                 x : x,
34995                 y : y + (this.unitHeight + this.gutter) * 2
34996             });
34997             
34998             pos.push({
34999                 x : x + (this.unitWidth + this.gutter) * 1,
35000                 y : y
35001             });
35002             
35003             return pos;
35004             
35005         }
35006         
35007         pos.push({
35008             x : x,
35009             y : y
35010         });
35011
35012         pos.push({
35013             x : x + (this.unitWidth + this.gutter) * 2,
35014             y : y
35015         });
35016
35017         pos.push({
35018             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35019             y : y + (this.unitHeight + this.gutter) * 1
35020         });
35021
35022         pos.push({
35023             x : x + (this.unitWidth + this.gutter) * 2,
35024             y : y + (this.unitWidth + this.gutter) * 2
35025         });
35026
35027         return pos;
35028         
35029     },
35030     
35031     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35032     {
35033         var pos = [];
35034         
35035         if(box[0].size == 'md-left'){
35036             pos.push({
35037                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35038                 y : minY
35039             });
35040             
35041             return pos;
35042         }
35043         
35044         if(box[0].size == 'md-right'){
35045             pos.push({
35046                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35047                 y : minY + (this.unitWidth + this.gutter) * 1
35048             });
35049             
35050             return pos;
35051         }
35052         
35053         var rand = Math.floor(Math.random() * (4 - box[0].y));
35054         
35055         pos.push({
35056             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35057             y : minY + (this.unitWidth + this.gutter) * rand
35058         });
35059         
35060         return pos;
35061         
35062     },
35063     
35064     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35065     {
35066         var pos = [];
35067         
35068         if(box[0].size == 'xs'){
35069             
35070             pos.push({
35071                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35072                 y : minY
35073             });
35074
35075             pos.push({
35076                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35077                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35078             });
35079             
35080             return pos;
35081             
35082         }
35083         
35084         pos.push({
35085             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35086             y : minY
35087         });
35088
35089         pos.push({
35090             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35091             y : minY + (this.unitWidth + this.gutter) * 2
35092         });
35093         
35094         return pos;
35095         
35096     },
35097     
35098     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35099     {
35100         var pos = [];
35101         
35102         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35103             
35104             pos.push({
35105                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35106                 y : minY
35107             });
35108
35109             pos.push({
35110                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35111                 y : minY + (this.unitWidth + this.gutter) * 1
35112             });
35113             
35114             pos.push({
35115                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35116                 y : minY + (this.unitWidth + this.gutter) * 2
35117             });
35118             
35119             return pos;
35120             
35121         }
35122         
35123         if(box[0].size == 'xs' && box[1].size == 'xs'){
35124             
35125             pos.push({
35126                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35127                 y : minY
35128             });
35129
35130             pos.push({
35131                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35132                 y : minY
35133             });
35134             
35135             pos.push({
35136                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35137                 y : minY + (this.unitWidth + this.gutter) * 1
35138             });
35139             
35140             return pos;
35141             
35142         }
35143         
35144         pos.push({
35145             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35146             y : minY
35147         });
35148
35149         pos.push({
35150             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35151             y : minY + (this.unitWidth + this.gutter) * 2
35152         });
35153
35154         pos.push({
35155             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35156             y : minY + (this.unitWidth + this.gutter) * 2
35157         });
35158             
35159         return pos;
35160         
35161     },
35162     
35163     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35164     {
35165         var pos = [];
35166         
35167         if(box[0].size == 'xs'){
35168             
35169             pos.push({
35170                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35171                 y : minY
35172             });
35173
35174             pos.push({
35175                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35176                 y : minY
35177             });
35178             
35179             pos.push({
35180                 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),
35181                 y : minY
35182             });
35183             
35184             pos.push({
35185                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35186                 y : minY + (this.unitWidth + this.gutter) * 1
35187             });
35188             
35189             return pos;
35190             
35191         }
35192         
35193         pos.push({
35194             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35195             y : minY
35196         });
35197         
35198         pos.push({
35199             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35200             y : minY + (this.unitWidth + this.gutter) * 2
35201         });
35202         
35203         pos.push({
35204             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35205             y : minY + (this.unitWidth + this.gutter) * 2
35206         });
35207         
35208         pos.push({
35209             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),
35210             y : minY + (this.unitWidth + this.gutter) * 2
35211         });
35212
35213         return pos;
35214         
35215     },
35216     
35217     /**
35218     * remove a Masonry Brick
35219     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35220     */
35221     removeBrick : function(brick_id)
35222     {
35223         if (!brick_id) {
35224             return;
35225         }
35226         
35227         for (var i = 0; i<this.bricks.length; i++) {
35228             if (this.bricks[i].id == brick_id) {
35229                 this.bricks.splice(i,1);
35230                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35231                 this.initial();
35232             }
35233         }
35234     },
35235     
35236     /**
35237     * adds a Masonry Brick
35238     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35239     */
35240     addBrick : function(cfg)
35241     {
35242         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35243         //this.register(cn);
35244         cn.parentId = this.id;
35245         cn.render(this.el);
35246         return cn;
35247     },
35248     
35249     /**
35250     * register a Masonry Brick
35251     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35252     */
35253     
35254     register : function(brick)
35255     {
35256         this.bricks.push(brick);
35257         brick.masonryId = this.id;
35258     },
35259     
35260     /**
35261     * clear all the Masonry Brick
35262     */
35263     clearAll : function()
35264     {
35265         this.bricks = [];
35266         //this.getChildContainer().dom.innerHTML = "";
35267         this.el.dom.innerHTML = '';
35268     },
35269     
35270     getSelected : function()
35271     {
35272         if (!this.selectedBrick) {
35273             return false;
35274         }
35275         
35276         return this.selectedBrick;
35277     }
35278 });
35279
35280 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35281     
35282     groups: {},
35283      /**
35284     * register a Masonry Layout
35285     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35286     */
35287     
35288     register : function(layout)
35289     {
35290         this.groups[layout.id] = layout;
35291     },
35292     /**
35293     * fetch a  Masonry Layout based on the masonry layout ID
35294     * @param {string} the masonry layout to add
35295     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35296     */
35297     
35298     get: function(layout_id) {
35299         if (typeof(this.groups[layout_id]) == 'undefined') {
35300             return false;
35301         }
35302         return this.groups[layout_id] ;
35303     }
35304     
35305     
35306     
35307 });
35308
35309  
35310
35311  /**
35312  *
35313  * This is based on 
35314  * http://masonry.desandro.com
35315  *
35316  * The idea is to render all the bricks based on vertical width...
35317  *
35318  * The original code extends 'outlayer' - we might need to use that....
35319  * 
35320  */
35321
35322
35323 /**
35324  * @class Roo.bootstrap.LayoutMasonryAuto
35325  * @extends Roo.bootstrap.Component
35326  * Bootstrap Layout Masonry class
35327  * 
35328  * @constructor
35329  * Create a new Element
35330  * @param {Object} config The config object
35331  */
35332
35333 Roo.bootstrap.LayoutMasonryAuto = function(config){
35334     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35335 };
35336
35337 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35338     
35339       /**
35340      * @cfg {Boolean} isFitWidth  - resize the width..
35341      */   
35342     isFitWidth : false,  // options..
35343     /**
35344      * @cfg {Boolean} isOriginLeft = left align?
35345      */   
35346     isOriginLeft : true,
35347     /**
35348      * @cfg {Boolean} isOriginTop = top align?
35349      */   
35350     isOriginTop : false,
35351     /**
35352      * @cfg {Boolean} isLayoutInstant = no animation?
35353      */   
35354     isLayoutInstant : false, // needed?
35355     /**
35356      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35357      */   
35358     isResizingContainer : true,
35359     /**
35360      * @cfg {Number} columnWidth  width of the columns 
35361      */   
35362     
35363     columnWidth : 0,
35364     
35365     /**
35366      * @cfg {Number} maxCols maximum number of columns
35367      */   
35368     
35369     maxCols: 0,
35370     /**
35371      * @cfg {Number} padHeight padding below box..
35372      */   
35373     
35374     padHeight : 10, 
35375     
35376     /**
35377      * @cfg {Boolean} isAutoInitial defalut true
35378      */   
35379     
35380     isAutoInitial : true, 
35381     
35382     // private?
35383     gutter : 0,
35384     
35385     containerWidth: 0,
35386     initialColumnWidth : 0,
35387     currentSize : null,
35388     
35389     colYs : null, // array.
35390     maxY : 0,
35391     padWidth: 10,
35392     
35393     
35394     tag: 'div',
35395     cls: '',
35396     bricks: null, //CompositeElement
35397     cols : 0, // array?
35398     // element : null, // wrapped now this.el
35399     _isLayoutInited : null, 
35400     
35401     
35402     getAutoCreate : function(){
35403         
35404         var cfg = {
35405             tag: this.tag,
35406             cls: 'blog-masonary-wrapper ' + this.cls,
35407             cn : {
35408                 cls : 'mas-boxes masonary'
35409             }
35410         };
35411         
35412         return cfg;
35413     },
35414     
35415     getChildContainer: function( )
35416     {
35417         if (this.boxesEl) {
35418             return this.boxesEl;
35419         }
35420         
35421         this.boxesEl = this.el.select('.mas-boxes').first();
35422         
35423         return this.boxesEl;
35424     },
35425     
35426     
35427     initEvents : function()
35428     {
35429         var _this = this;
35430         
35431         if(this.isAutoInitial){
35432             Roo.log('hook children rendered');
35433             this.on('childrenrendered', function() {
35434                 Roo.log('children rendered');
35435                 _this.initial();
35436             } ,this);
35437         }
35438         
35439     },
35440     
35441     initial : function()
35442     {
35443         this.reloadItems();
35444
35445         this.currentSize = this.el.getBox(true);
35446
35447         /// was window resize... - let's see if this works..
35448         Roo.EventManager.onWindowResize(this.resize, this); 
35449
35450         if(!this.isAutoInitial){
35451             this.layout();
35452             return;
35453         }
35454         
35455         this.layout.defer(500,this);
35456     },
35457     
35458     reloadItems: function()
35459     {
35460         this.bricks = this.el.select('.masonry-brick', true);
35461         
35462         this.bricks.each(function(b) {
35463             //Roo.log(b.getSize());
35464             if (!b.attr('originalwidth')) {
35465                 b.attr('originalwidth',  b.getSize().width);
35466             }
35467             
35468         });
35469         
35470         Roo.log(this.bricks.elements.length);
35471     },
35472     
35473     resize : function()
35474     {
35475         Roo.log('resize');
35476         var cs = this.el.getBox(true);
35477         
35478         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35479             Roo.log("no change in with or X");
35480             return;
35481         }
35482         this.currentSize = cs;
35483         this.layout();
35484     },
35485     
35486     layout : function()
35487     {
35488          Roo.log('layout');
35489         this._resetLayout();
35490         //this._manageStamps();
35491       
35492         // don't animate first layout
35493         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35494         this.layoutItems( isInstant );
35495       
35496         // flag for initalized
35497         this._isLayoutInited = true;
35498     },
35499     
35500     layoutItems : function( isInstant )
35501     {
35502         //var items = this._getItemsForLayout( this.items );
35503         // original code supports filtering layout items.. we just ignore it..
35504         
35505         this._layoutItems( this.bricks , isInstant );
35506       
35507         this._postLayout();
35508     },
35509     _layoutItems : function ( items , isInstant)
35510     {
35511        //this.fireEvent( 'layout', this, items );
35512     
35513
35514         if ( !items || !items.elements.length ) {
35515           // no items, emit event with empty array
35516             return;
35517         }
35518
35519         var queue = [];
35520         items.each(function(item) {
35521             Roo.log("layout item");
35522             Roo.log(item);
35523             // get x/y object from method
35524             var position = this._getItemLayoutPosition( item );
35525             // enqueue
35526             position.item = item;
35527             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35528             queue.push( position );
35529         }, this);
35530       
35531         this._processLayoutQueue( queue );
35532     },
35533     /** Sets position of item in DOM
35534     * @param {Element} item
35535     * @param {Number} x - horizontal position
35536     * @param {Number} y - vertical position
35537     * @param {Boolean} isInstant - disables transitions
35538     */
35539     _processLayoutQueue : function( queue )
35540     {
35541         for ( var i=0, len = queue.length; i < len; i++ ) {
35542             var obj = queue[i];
35543             obj.item.position('absolute');
35544             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35545         }
35546     },
35547       
35548     
35549     /**
35550     * Any logic you want to do after each layout,
35551     * i.e. size the container
35552     */
35553     _postLayout : function()
35554     {
35555         this.resizeContainer();
35556     },
35557     
35558     resizeContainer : function()
35559     {
35560         if ( !this.isResizingContainer ) {
35561             return;
35562         }
35563         var size = this._getContainerSize();
35564         if ( size ) {
35565             this.el.setSize(size.width,size.height);
35566             this.boxesEl.setSize(size.width,size.height);
35567         }
35568     },
35569     
35570     
35571     
35572     _resetLayout : function()
35573     {
35574         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35575         this.colWidth = this.el.getWidth();
35576         //this.gutter = this.el.getWidth(); 
35577         
35578         this.measureColumns();
35579
35580         // reset column Y
35581         var i = this.cols;
35582         this.colYs = [];
35583         while (i--) {
35584             this.colYs.push( 0 );
35585         }
35586     
35587         this.maxY = 0;
35588     },
35589
35590     measureColumns : function()
35591     {
35592         this.getContainerWidth();
35593       // if columnWidth is 0, default to outerWidth of first item
35594         if ( !this.columnWidth ) {
35595             var firstItem = this.bricks.first();
35596             Roo.log(firstItem);
35597             this.columnWidth  = this.containerWidth;
35598             if (firstItem && firstItem.attr('originalwidth') ) {
35599                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35600             }
35601             // columnWidth fall back to item of first element
35602             Roo.log("set column width?");
35603                         this.initialColumnWidth = this.columnWidth  ;
35604
35605             // if first elem has no width, default to size of container
35606             
35607         }
35608         
35609         
35610         if (this.initialColumnWidth) {
35611             this.columnWidth = this.initialColumnWidth;
35612         }
35613         
35614         
35615             
35616         // column width is fixed at the top - however if container width get's smaller we should
35617         // reduce it...
35618         
35619         // this bit calcs how man columns..
35620             
35621         var columnWidth = this.columnWidth += this.gutter;
35622       
35623         // calculate columns
35624         var containerWidth = this.containerWidth + this.gutter;
35625         
35626         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35627         // fix rounding errors, typically with gutters
35628         var excess = columnWidth - containerWidth % columnWidth;
35629         
35630         
35631         // if overshoot is less than a pixel, round up, otherwise floor it
35632         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35633         cols = Math[ mathMethod ]( cols );
35634         this.cols = Math.max( cols, 1 );
35635         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35636         
35637          // padding positioning..
35638         var totalColWidth = this.cols * this.columnWidth;
35639         var padavail = this.containerWidth - totalColWidth;
35640         // so for 2 columns - we need 3 'pads'
35641         
35642         var padNeeded = (1+this.cols) * this.padWidth;
35643         
35644         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35645         
35646         this.columnWidth += padExtra
35647         //this.padWidth = Math.floor(padavail /  ( this.cols));
35648         
35649         // adjust colum width so that padding is fixed??
35650         
35651         // we have 3 columns ... total = width * 3
35652         // we have X left over... that should be used by 
35653         
35654         //if (this.expandC) {
35655             
35656         //}
35657         
35658         
35659         
35660     },
35661     
35662     getContainerWidth : function()
35663     {
35664        /* // container is parent if fit width
35665         var container = this.isFitWidth ? this.element.parentNode : this.element;
35666         // check that this.size and size are there
35667         // IE8 triggers resize on body size change, so they might not be
35668         
35669         var size = getSize( container );  //FIXME
35670         this.containerWidth = size && size.innerWidth; //FIXME
35671         */
35672          
35673         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35674         
35675     },
35676     
35677     _getItemLayoutPosition : function( item )  // what is item?
35678     {
35679         // we resize the item to our columnWidth..
35680       
35681         item.setWidth(this.columnWidth);
35682         item.autoBoxAdjust  = false;
35683         
35684         var sz = item.getSize();
35685  
35686         // how many columns does this brick span
35687         var remainder = this.containerWidth % this.columnWidth;
35688         
35689         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35690         // round if off by 1 pixel, otherwise use ceil
35691         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35692         colSpan = Math.min( colSpan, this.cols );
35693         
35694         // normally this should be '1' as we dont' currently allow multi width columns..
35695         
35696         var colGroup = this._getColGroup( colSpan );
35697         // get the minimum Y value from the columns
35698         var minimumY = Math.min.apply( Math, colGroup );
35699         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35700         
35701         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35702          
35703         // position the brick
35704         var position = {
35705             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35706             y: this.currentSize.y + minimumY + this.padHeight
35707         };
35708         
35709         Roo.log(position);
35710         // apply setHeight to necessary columns
35711         var setHeight = minimumY + sz.height + this.padHeight;
35712         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35713         
35714         var setSpan = this.cols + 1 - colGroup.length;
35715         for ( var i = 0; i < setSpan; i++ ) {
35716           this.colYs[ shortColIndex + i ] = setHeight ;
35717         }
35718       
35719         return position;
35720     },
35721     
35722     /**
35723      * @param {Number} colSpan - number of columns the element spans
35724      * @returns {Array} colGroup
35725      */
35726     _getColGroup : function( colSpan )
35727     {
35728         if ( colSpan < 2 ) {
35729           // if brick spans only one column, use all the column Ys
35730           return this.colYs;
35731         }
35732       
35733         var colGroup = [];
35734         // how many different places could this brick fit horizontally
35735         var groupCount = this.cols + 1 - colSpan;
35736         // for each group potential horizontal position
35737         for ( var i = 0; i < groupCount; i++ ) {
35738           // make an array of colY values for that one group
35739           var groupColYs = this.colYs.slice( i, i + colSpan );
35740           // and get the max value of the array
35741           colGroup[i] = Math.max.apply( Math, groupColYs );
35742         }
35743         return colGroup;
35744     },
35745     /*
35746     _manageStamp : function( stamp )
35747     {
35748         var stampSize =  stamp.getSize();
35749         var offset = stamp.getBox();
35750         // get the columns that this stamp affects
35751         var firstX = this.isOriginLeft ? offset.x : offset.right;
35752         var lastX = firstX + stampSize.width;
35753         var firstCol = Math.floor( firstX / this.columnWidth );
35754         firstCol = Math.max( 0, firstCol );
35755         
35756         var lastCol = Math.floor( lastX / this.columnWidth );
35757         // lastCol should not go over if multiple of columnWidth #425
35758         lastCol -= lastX % this.columnWidth ? 0 : 1;
35759         lastCol = Math.min( this.cols - 1, lastCol );
35760         
35761         // set colYs to bottom of the stamp
35762         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35763             stampSize.height;
35764             
35765         for ( var i = firstCol; i <= lastCol; i++ ) {
35766           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35767         }
35768     },
35769     */
35770     
35771     _getContainerSize : function()
35772     {
35773         this.maxY = Math.max.apply( Math, this.colYs );
35774         var size = {
35775             height: this.maxY
35776         };
35777       
35778         if ( this.isFitWidth ) {
35779             size.width = this._getContainerFitWidth();
35780         }
35781       
35782         return size;
35783     },
35784     
35785     _getContainerFitWidth : function()
35786     {
35787         var unusedCols = 0;
35788         // count unused columns
35789         var i = this.cols;
35790         while ( --i ) {
35791           if ( this.colYs[i] !== 0 ) {
35792             break;
35793           }
35794           unusedCols++;
35795         }
35796         // fit container to columns that have been used
35797         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35798     },
35799     
35800     needsResizeLayout : function()
35801     {
35802         var previousWidth = this.containerWidth;
35803         this.getContainerWidth();
35804         return previousWidth !== this.containerWidth;
35805     }
35806  
35807 });
35808
35809  
35810
35811  /*
35812  * - LGPL
35813  *
35814  * element
35815  * 
35816  */
35817
35818 /**
35819  * @class Roo.bootstrap.MasonryBrick
35820  * @extends Roo.bootstrap.Component
35821  * Bootstrap MasonryBrick class
35822  * 
35823  * @constructor
35824  * Create a new MasonryBrick
35825  * @param {Object} config The config object
35826  */
35827
35828 Roo.bootstrap.MasonryBrick = function(config){
35829     
35830     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35831     
35832     Roo.bootstrap.MasonryBrick.register(this);
35833     
35834     this.addEvents({
35835         // raw events
35836         /**
35837          * @event click
35838          * When a MasonryBrick is clcik
35839          * @param {Roo.bootstrap.MasonryBrick} this
35840          * @param {Roo.EventObject} e
35841          */
35842         "click" : true
35843     });
35844 };
35845
35846 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35847     
35848     /**
35849      * @cfg {String} title
35850      */   
35851     title : '',
35852     /**
35853      * @cfg {String} html
35854      */   
35855     html : '',
35856     /**
35857      * @cfg {String} bgimage
35858      */   
35859     bgimage : '',
35860     /**
35861      * @cfg {String} videourl
35862      */   
35863     videourl : '',
35864     /**
35865      * @cfg {String} cls
35866      */   
35867     cls : '',
35868     /**
35869      * @cfg {String} href
35870      */   
35871     href : '',
35872     /**
35873      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35874      */   
35875     size : 'xs',
35876     
35877     /**
35878      * @cfg {String} placetitle (center|bottom)
35879      */   
35880     placetitle : '',
35881     
35882     /**
35883      * @cfg {Boolean} isFitContainer defalut true
35884      */   
35885     isFitContainer : true, 
35886     
35887     /**
35888      * @cfg {Boolean} preventDefault defalut false
35889      */   
35890     preventDefault : false, 
35891     
35892     /**
35893      * @cfg {Boolean} inverse defalut false
35894      */   
35895     maskInverse : false, 
35896     
35897     getAutoCreate : function()
35898     {
35899         if(!this.isFitContainer){
35900             return this.getSplitAutoCreate();
35901         }
35902         
35903         var cls = 'masonry-brick masonry-brick-full';
35904         
35905         if(this.href.length){
35906             cls += ' masonry-brick-link';
35907         }
35908         
35909         if(this.bgimage.length){
35910             cls += ' masonry-brick-image';
35911         }
35912         
35913         if(this.maskInverse){
35914             cls += ' mask-inverse';
35915         }
35916         
35917         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35918             cls += ' enable-mask';
35919         }
35920         
35921         if(this.size){
35922             cls += ' masonry-' + this.size + '-brick';
35923         }
35924         
35925         if(this.placetitle.length){
35926             
35927             switch (this.placetitle) {
35928                 case 'center' :
35929                     cls += ' masonry-center-title';
35930                     break;
35931                 case 'bottom' :
35932                     cls += ' masonry-bottom-title';
35933                     break;
35934                 default:
35935                     break;
35936             }
35937             
35938         } else {
35939             if(!this.html.length && !this.bgimage.length){
35940                 cls += ' masonry-center-title';
35941             }
35942
35943             if(!this.html.length && this.bgimage.length){
35944                 cls += ' masonry-bottom-title';
35945             }
35946         }
35947         
35948         if(this.cls){
35949             cls += ' ' + this.cls;
35950         }
35951         
35952         var cfg = {
35953             tag: (this.href.length) ? 'a' : 'div',
35954             cls: cls,
35955             cn: [
35956                 {
35957                     tag: 'div',
35958                     cls: 'masonry-brick-mask'
35959                 },
35960                 {
35961                     tag: 'div',
35962                     cls: 'masonry-brick-paragraph',
35963                     cn: []
35964                 }
35965             ]
35966         };
35967         
35968         if(this.href.length){
35969             cfg.href = this.href;
35970         }
35971         
35972         var cn = cfg.cn[1].cn;
35973         
35974         if(this.title.length){
35975             cn.push({
35976                 tag: 'h4',
35977                 cls: 'masonry-brick-title',
35978                 html: this.title
35979             });
35980         }
35981         
35982         if(this.html.length){
35983             cn.push({
35984                 tag: 'p',
35985                 cls: 'masonry-brick-text',
35986                 html: this.html
35987             });
35988         }
35989         
35990         if (!this.title.length && !this.html.length) {
35991             cfg.cn[1].cls += ' hide';
35992         }
35993         
35994         if(this.bgimage.length){
35995             cfg.cn.push({
35996                 tag: 'img',
35997                 cls: 'masonry-brick-image-view',
35998                 src: this.bgimage
35999             });
36000         }
36001         
36002         if(this.videourl.length){
36003             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36004             // youtube support only?
36005             cfg.cn.push({
36006                 tag: 'iframe',
36007                 cls: 'masonry-brick-image-view',
36008                 src: vurl,
36009                 frameborder : 0,
36010                 allowfullscreen : true
36011             });
36012         }
36013         
36014         return cfg;
36015         
36016     },
36017     
36018     getSplitAutoCreate : function()
36019     {
36020         var cls = 'masonry-brick masonry-brick-split';
36021         
36022         if(this.href.length){
36023             cls += ' masonry-brick-link';
36024         }
36025         
36026         if(this.bgimage.length){
36027             cls += ' masonry-brick-image';
36028         }
36029         
36030         if(this.size){
36031             cls += ' masonry-' + this.size + '-brick';
36032         }
36033         
36034         switch (this.placetitle) {
36035             case 'center' :
36036                 cls += ' masonry-center-title';
36037                 break;
36038             case 'bottom' :
36039                 cls += ' masonry-bottom-title';
36040                 break;
36041             default:
36042                 if(!this.bgimage.length){
36043                     cls += ' masonry-center-title';
36044                 }
36045
36046                 if(this.bgimage.length){
36047                     cls += ' masonry-bottom-title';
36048                 }
36049                 break;
36050         }
36051         
36052         if(this.cls){
36053             cls += ' ' + this.cls;
36054         }
36055         
36056         var cfg = {
36057             tag: (this.href.length) ? 'a' : 'div',
36058             cls: cls,
36059             cn: [
36060                 {
36061                     tag: 'div',
36062                     cls: 'masonry-brick-split-head',
36063                     cn: [
36064                         {
36065                             tag: 'div',
36066                             cls: 'masonry-brick-paragraph',
36067                             cn: []
36068                         }
36069                     ]
36070                 },
36071                 {
36072                     tag: 'div',
36073                     cls: 'masonry-brick-split-body',
36074                     cn: []
36075                 }
36076             ]
36077         };
36078         
36079         if(this.href.length){
36080             cfg.href = this.href;
36081         }
36082         
36083         if(this.title.length){
36084             cfg.cn[0].cn[0].cn.push({
36085                 tag: 'h4',
36086                 cls: 'masonry-brick-title',
36087                 html: this.title
36088             });
36089         }
36090         
36091         if(this.html.length){
36092             cfg.cn[1].cn.push({
36093                 tag: 'p',
36094                 cls: 'masonry-brick-text',
36095                 html: this.html
36096             });
36097         }
36098
36099         if(this.bgimage.length){
36100             cfg.cn[0].cn.push({
36101                 tag: 'img',
36102                 cls: 'masonry-brick-image-view',
36103                 src: this.bgimage
36104             });
36105         }
36106         
36107         if(this.videourl.length){
36108             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36109             // youtube support only?
36110             cfg.cn[0].cn.cn.push({
36111                 tag: 'iframe',
36112                 cls: 'masonry-brick-image-view',
36113                 src: vurl,
36114                 frameborder : 0,
36115                 allowfullscreen : true
36116             });
36117         }
36118         
36119         return cfg;
36120     },
36121     
36122     initEvents: function() 
36123     {
36124         switch (this.size) {
36125             case 'xs' :
36126                 this.x = 1;
36127                 this.y = 1;
36128                 break;
36129             case 'sm' :
36130                 this.x = 2;
36131                 this.y = 2;
36132                 break;
36133             case 'md' :
36134             case 'md-left' :
36135             case 'md-right' :
36136                 this.x = 3;
36137                 this.y = 3;
36138                 break;
36139             case 'tall' :
36140                 this.x = 2;
36141                 this.y = 3;
36142                 break;
36143             case 'wide' :
36144                 this.x = 3;
36145                 this.y = 2;
36146                 break;
36147             case 'wide-thin' :
36148                 this.x = 3;
36149                 this.y = 1;
36150                 break;
36151                         
36152             default :
36153                 break;
36154         }
36155         
36156         if(Roo.isTouch){
36157             this.el.on('touchstart', this.onTouchStart, this);
36158             this.el.on('touchmove', this.onTouchMove, this);
36159             this.el.on('touchend', this.onTouchEnd, this);
36160             this.el.on('contextmenu', this.onContextMenu, this);
36161         } else {
36162             this.el.on('mouseenter'  ,this.enter, this);
36163             this.el.on('mouseleave', this.leave, this);
36164             this.el.on('click', this.onClick, this);
36165         }
36166         
36167         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36168             this.parent().bricks.push(this);   
36169         }
36170         
36171     },
36172     
36173     onClick: function(e, el)
36174     {
36175         var time = this.endTimer - this.startTimer;
36176         // Roo.log(e.preventDefault());
36177         if(Roo.isTouch){
36178             if(time > 1000){
36179                 e.preventDefault();
36180                 return;
36181             }
36182         }
36183         
36184         if(!this.preventDefault){
36185             return;
36186         }
36187         
36188         e.preventDefault();
36189         
36190         if (this.activeClass != '') {
36191             this.selectBrick();
36192         }
36193         
36194         this.fireEvent('click', this, e);
36195     },
36196     
36197     enter: function(e, el)
36198     {
36199         e.preventDefault();
36200         
36201         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36202             return;
36203         }
36204         
36205         if(this.bgimage.length && this.html.length){
36206             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36207         }
36208     },
36209     
36210     leave: function(e, el)
36211     {
36212         e.preventDefault();
36213         
36214         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36215             return;
36216         }
36217         
36218         if(this.bgimage.length && this.html.length){
36219             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36220         }
36221     },
36222     
36223     onTouchStart: function(e, el)
36224     {
36225 //        e.preventDefault();
36226         
36227         this.touchmoved = false;
36228         
36229         if(!this.isFitContainer){
36230             return;
36231         }
36232         
36233         if(!this.bgimage.length || !this.html.length){
36234             return;
36235         }
36236         
36237         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36238         
36239         this.timer = new Date().getTime();
36240         
36241     },
36242     
36243     onTouchMove: function(e, el)
36244     {
36245         this.touchmoved = true;
36246     },
36247     
36248     onContextMenu : function(e,el)
36249     {
36250         e.preventDefault();
36251         e.stopPropagation();
36252         return false;
36253     },
36254     
36255     onTouchEnd: function(e, el)
36256     {
36257 //        e.preventDefault();
36258         
36259         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36260         
36261             this.leave(e,el);
36262             
36263             return;
36264         }
36265         
36266         if(!this.bgimage.length || !this.html.length){
36267             
36268             if(this.href.length){
36269                 window.location.href = this.href;
36270             }
36271             
36272             return;
36273         }
36274         
36275         if(!this.isFitContainer){
36276             return;
36277         }
36278         
36279         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36280         
36281         window.location.href = this.href;
36282     },
36283     
36284     //selection on single brick only
36285     selectBrick : function() {
36286         
36287         if (!this.parentId) {
36288             return;
36289         }
36290         
36291         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36292         var index = m.selectedBrick.indexOf(this.id);
36293         
36294         if ( index > -1) {
36295             m.selectedBrick.splice(index,1);
36296             this.el.removeClass(this.activeClass);
36297             return;
36298         }
36299         
36300         for(var i = 0; i < m.selectedBrick.length; i++) {
36301             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36302             b.el.removeClass(b.activeClass);
36303         }
36304         
36305         m.selectedBrick = [];
36306         
36307         m.selectedBrick.push(this.id);
36308         this.el.addClass(this.activeClass);
36309         return;
36310     },
36311     
36312     isSelected : function(){
36313         return this.el.hasClass(this.activeClass);
36314         
36315     }
36316 });
36317
36318 Roo.apply(Roo.bootstrap.MasonryBrick, {
36319     
36320     //groups: {},
36321     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36322      /**
36323     * register a Masonry Brick
36324     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36325     */
36326     
36327     register : function(brick)
36328     {
36329         //this.groups[brick.id] = brick;
36330         this.groups.add(brick.id, brick);
36331     },
36332     /**
36333     * fetch a  masonry brick based on the masonry brick ID
36334     * @param {string} the masonry brick to add
36335     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36336     */
36337     
36338     get: function(brick_id) 
36339     {
36340         // if (typeof(this.groups[brick_id]) == 'undefined') {
36341         //     return false;
36342         // }
36343         // return this.groups[brick_id] ;
36344         
36345         if(this.groups.key(brick_id)) {
36346             return this.groups.key(brick_id);
36347         }
36348         
36349         return false;
36350     }
36351     
36352     
36353     
36354 });
36355
36356  /*
36357  * - LGPL
36358  *
36359  * element
36360  * 
36361  */
36362
36363 /**
36364  * @class Roo.bootstrap.Brick
36365  * @extends Roo.bootstrap.Component
36366  * Bootstrap Brick class
36367  * 
36368  * @constructor
36369  * Create a new Brick
36370  * @param {Object} config The config object
36371  */
36372
36373 Roo.bootstrap.Brick = function(config){
36374     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36375     
36376     this.addEvents({
36377         // raw events
36378         /**
36379          * @event click
36380          * When a Brick is click
36381          * @param {Roo.bootstrap.Brick} this
36382          * @param {Roo.EventObject} e
36383          */
36384         "click" : true
36385     });
36386 };
36387
36388 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36389     
36390     /**
36391      * @cfg {String} title
36392      */   
36393     title : '',
36394     /**
36395      * @cfg {String} html
36396      */   
36397     html : '',
36398     /**
36399      * @cfg {String} bgimage
36400      */   
36401     bgimage : '',
36402     /**
36403      * @cfg {String} cls
36404      */   
36405     cls : '',
36406     /**
36407      * @cfg {String} href
36408      */   
36409     href : '',
36410     /**
36411      * @cfg {String} video
36412      */   
36413     video : '',
36414     /**
36415      * @cfg {Boolean} square
36416      */   
36417     square : true,
36418     
36419     getAutoCreate : function()
36420     {
36421         var cls = 'roo-brick';
36422         
36423         if(this.href.length){
36424             cls += ' roo-brick-link';
36425         }
36426         
36427         if(this.bgimage.length){
36428             cls += ' roo-brick-image';
36429         }
36430         
36431         if(!this.html.length && !this.bgimage.length){
36432             cls += ' roo-brick-center-title';
36433         }
36434         
36435         if(!this.html.length && this.bgimage.length){
36436             cls += ' roo-brick-bottom-title';
36437         }
36438         
36439         if(this.cls){
36440             cls += ' ' + this.cls;
36441         }
36442         
36443         var cfg = {
36444             tag: (this.href.length) ? 'a' : 'div',
36445             cls: cls,
36446             cn: [
36447                 {
36448                     tag: 'div',
36449                     cls: 'roo-brick-paragraph',
36450                     cn: []
36451                 }
36452             ]
36453         };
36454         
36455         if(this.href.length){
36456             cfg.href = this.href;
36457         }
36458         
36459         var cn = cfg.cn[0].cn;
36460         
36461         if(this.title.length){
36462             cn.push({
36463                 tag: 'h4',
36464                 cls: 'roo-brick-title',
36465                 html: this.title
36466             });
36467         }
36468         
36469         if(this.html.length){
36470             cn.push({
36471                 tag: 'p',
36472                 cls: 'roo-brick-text',
36473                 html: this.html
36474             });
36475         } else {
36476             cn.cls += ' hide';
36477         }
36478         
36479         if(this.bgimage.length){
36480             cfg.cn.push({
36481                 tag: 'img',
36482                 cls: 'roo-brick-image-view',
36483                 src: this.bgimage
36484             });
36485         }
36486         
36487         return cfg;
36488     },
36489     
36490     initEvents: function() 
36491     {
36492         if(this.title.length || this.html.length){
36493             this.el.on('mouseenter'  ,this.enter, this);
36494             this.el.on('mouseleave', this.leave, this);
36495         }
36496         
36497         Roo.EventManager.onWindowResize(this.resize, this); 
36498         
36499         if(this.bgimage.length){
36500             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36501             this.imageEl.on('load', this.onImageLoad, this);
36502             return;
36503         }
36504         
36505         this.resize();
36506     },
36507     
36508     onImageLoad : function()
36509     {
36510         this.resize();
36511     },
36512     
36513     resize : function()
36514     {
36515         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36516         
36517         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36518         
36519         if(this.bgimage.length){
36520             var image = this.el.select('.roo-brick-image-view', true).first();
36521             
36522             image.setWidth(paragraph.getWidth());
36523             
36524             if(this.square){
36525                 image.setHeight(paragraph.getWidth());
36526             }
36527             
36528             this.el.setHeight(image.getHeight());
36529             paragraph.setHeight(image.getHeight());
36530             
36531         }
36532         
36533     },
36534     
36535     enter: function(e, el)
36536     {
36537         e.preventDefault();
36538         
36539         if(this.bgimage.length){
36540             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36541             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36542         }
36543     },
36544     
36545     leave: function(e, el)
36546     {
36547         e.preventDefault();
36548         
36549         if(this.bgimage.length){
36550             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36551             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36552         }
36553     }
36554     
36555 });
36556
36557  
36558
36559  /*
36560  * - LGPL
36561  *
36562  * Number field 
36563  */
36564
36565 /**
36566  * @class Roo.bootstrap.form.NumberField
36567  * @extends Roo.bootstrap.form.Input
36568  * Bootstrap NumberField class
36569  * 
36570  * 
36571  * 
36572  * 
36573  * @constructor
36574  * Create a new NumberField
36575  * @param {Object} config The config object
36576  */
36577
36578 Roo.bootstrap.form.NumberField = function(config){
36579     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36580 };
36581
36582 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36583     
36584     /**
36585      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36586      */
36587     allowDecimals : true,
36588     /**
36589      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36590      */
36591     decimalSeparator : ".",
36592     /**
36593      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36594      */
36595     decimalPrecision : 2,
36596     /**
36597      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36598      */
36599     allowNegative : true,
36600     
36601     /**
36602      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36603      */
36604     allowZero: true,
36605     /**
36606      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36607      */
36608     minValue : Number.NEGATIVE_INFINITY,
36609     /**
36610      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36611      */
36612     maxValue : Number.MAX_VALUE,
36613     /**
36614      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36615      */
36616     minText : "The minimum value for this field is {0}",
36617     /**
36618      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36619      */
36620     maxText : "The maximum value for this field is {0}",
36621     /**
36622      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36623      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36624      */
36625     nanText : "{0} is not a valid number",
36626     /**
36627      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36628      */
36629     thousandsDelimiter : false,
36630     /**
36631      * @cfg {String} valueAlign alignment of value
36632      */
36633     valueAlign : "left",
36634
36635     getAutoCreate : function()
36636     {
36637         var hiddenInput = {
36638             tag: 'input',
36639             type: 'hidden',
36640             id: Roo.id(),
36641             cls: 'hidden-number-input'
36642         };
36643         
36644         if (this.name) {
36645             hiddenInput.name = this.name;
36646         }
36647         
36648         this.name = '';
36649         
36650         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36651         
36652         this.name = hiddenInput.name;
36653         
36654         if(cfg.cn.length > 0) {
36655             cfg.cn.push(hiddenInput);
36656         }
36657         
36658         return cfg;
36659     },
36660
36661     // private
36662     initEvents : function()
36663     {   
36664         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36665         
36666         var allowed = "0123456789";
36667         
36668         if(this.allowDecimals){
36669             allowed += this.decimalSeparator;
36670         }
36671         
36672         if(this.allowNegative){
36673             allowed += "-";
36674         }
36675         
36676         if(this.thousandsDelimiter) {
36677             allowed += ",";
36678         }
36679         
36680         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36681         
36682         var keyPress = function(e){
36683             
36684             var k = e.getKey();
36685             
36686             var c = e.getCharCode();
36687             
36688             if(
36689                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36690                     allowed.indexOf(String.fromCharCode(c)) === -1
36691             ){
36692                 e.stopEvent();
36693                 return;
36694             }
36695             
36696             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36697                 return;
36698             }
36699             
36700             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36701                 e.stopEvent();
36702             }
36703         };
36704         
36705         this.el.on("keypress", keyPress, this);
36706     },
36707     
36708     validateValue : function(value)
36709     {
36710         
36711         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36712             return false;
36713         }
36714         
36715         var num = this.parseValue(value);
36716         
36717         if(isNaN(num)){
36718             this.markInvalid(String.format(this.nanText, value));
36719             return false;
36720         }
36721         
36722         if(num < this.minValue){
36723             this.markInvalid(String.format(this.minText, this.minValue));
36724             return false;
36725         }
36726         
36727         if(num > this.maxValue){
36728             this.markInvalid(String.format(this.maxText, this.maxValue));
36729             return false;
36730         }
36731         
36732         return true;
36733     },
36734
36735     getValue : function()
36736     {
36737         var v = this.hiddenEl().getValue();
36738         
36739         return this.fixPrecision(this.parseValue(v));
36740     },
36741
36742     parseValue : function(value)
36743     {
36744         if(this.thousandsDelimiter) {
36745             value += "";
36746             r = new RegExp(",", "g");
36747             value = value.replace(r, "");
36748         }
36749         
36750         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36751         return isNaN(value) ? '' : value;
36752     },
36753
36754     fixPrecision : function(value)
36755     {
36756         if(this.thousandsDelimiter) {
36757             value += "";
36758             r = new RegExp(",", "g");
36759             value = value.replace(r, "");
36760         }
36761         
36762         var nan = isNaN(value);
36763         
36764         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36765             return nan ? '' : value;
36766         }
36767         return parseFloat(value).toFixed(this.decimalPrecision);
36768     },
36769
36770     setValue : function(v)
36771     {
36772         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36773         
36774         this.value = v;
36775         
36776         if(this.rendered){
36777             
36778             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36779             
36780             this.inputEl().dom.value = (v == '') ? '' :
36781                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36782             
36783             if(!this.allowZero && v === '0') {
36784                 this.hiddenEl().dom.value = '';
36785                 this.inputEl().dom.value = '';
36786             }
36787             
36788             this.validate();
36789         }
36790     },
36791
36792     decimalPrecisionFcn : function(v)
36793     {
36794         return Math.floor(v);
36795     },
36796
36797     beforeBlur : function()
36798     {
36799         var v = this.parseValue(this.getRawValue());
36800         
36801         if(v || v === 0 || v === ''){
36802             this.setValue(v);
36803         }
36804     },
36805     
36806     hiddenEl : function()
36807     {
36808         return this.el.select('input.hidden-number-input',true).first();
36809     }
36810     
36811 });
36812
36813  
36814
36815 /*
36816 * Licence: LGPL
36817 */
36818
36819 /**
36820  * @class Roo.bootstrap.DocumentSlider
36821  * @extends Roo.bootstrap.Component
36822  * Bootstrap DocumentSlider class
36823  * 
36824  * @constructor
36825  * Create a new DocumentViewer
36826  * @param {Object} config The config object
36827  */
36828
36829 Roo.bootstrap.DocumentSlider = function(config){
36830     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36831     
36832     this.files = [];
36833     
36834     this.addEvents({
36835         /**
36836          * @event initial
36837          * Fire after initEvent
36838          * @param {Roo.bootstrap.DocumentSlider} this
36839          */
36840         "initial" : true,
36841         /**
36842          * @event update
36843          * Fire after update
36844          * @param {Roo.bootstrap.DocumentSlider} this
36845          */
36846         "update" : true,
36847         /**
36848          * @event click
36849          * Fire after click
36850          * @param {Roo.bootstrap.DocumentSlider} this
36851          */
36852         "click" : true
36853     });
36854 };
36855
36856 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36857     
36858     files : false,
36859     
36860     indicator : 0,
36861     
36862     getAutoCreate : function()
36863     {
36864         var cfg = {
36865             tag : 'div',
36866             cls : 'roo-document-slider',
36867             cn : [
36868                 {
36869                     tag : 'div',
36870                     cls : 'roo-document-slider-header',
36871                     cn : [
36872                         {
36873                             tag : 'div',
36874                             cls : 'roo-document-slider-header-title'
36875                         }
36876                     ]
36877                 },
36878                 {
36879                     tag : 'div',
36880                     cls : 'roo-document-slider-body',
36881                     cn : [
36882                         {
36883                             tag : 'div',
36884                             cls : 'roo-document-slider-prev',
36885                             cn : [
36886                                 {
36887                                     tag : 'i',
36888                                     cls : 'fa fa-chevron-left'
36889                                 }
36890                             ]
36891                         },
36892                         {
36893                             tag : 'div',
36894                             cls : 'roo-document-slider-thumb',
36895                             cn : [
36896                                 {
36897                                     tag : 'img',
36898                                     cls : 'roo-document-slider-image'
36899                                 }
36900                             ]
36901                         },
36902                         {
36903                             tag : 'div',
36904                             cls : 'roo-document-slider-next',
36905                             cn : [
36906                                 {
36907                                     tag : 'i',
36908                                     cls : 'fa fa-chevron-right'
36909                                 }
36910                             ]
36911                         }
36912                     ]
36913                 }
36914             ]
36915         };
36916         
36917         return cfg;
36918     },
36919     
36920     initEvents : function()
36921     {
36922         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36923         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36924         
36925         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36926         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36927         
36928         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36929         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36930         
36931         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36932         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36933         
36934         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36935         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36936         
36937         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36938         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36939         
36940         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36941         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36942         
36943         this.thumbEl.on('click', this.onClick, this);
36944         
36945         this.prevIndicator.on('click', this.prev, this);
36946         
36947         this.nextIndicator.on('click', this.next, this);
36948         
36949     },
36950     
36951     initial : function()
36952     {
36953         if(this.files.length){
36954             this.indicator = 1;
36955             this.update()
36956         }
36957         
36958         this.fireEvent('initial', this);
36959     },
36960     
36961     update : function()
36962     {
36963         this.imageEl.attr('src', this.files[this.indicator - 1]);
36964         
36965         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36966         
36967         this.prevIndicator.show();
36968         
36969         if(this.indicator == 1){
36970             this.prevIndicator.hide();
36971         }
36972         
36973         this.nextIndicator.show();
36974         
36975         if(this.indicator == this.files.length){
36976             this.nextIndicator.hide();
36977         }
36978         
36979         this.thumbEl.scrollTo('top');
36980         
36981         this.fireEvent('update', this);
36982     },
36983     
36984     onClick : function(e)
36985     {
36986         e.preventDefault();
36987         
36988         this.fireEvent('click', this);
36989     },
36990     
36991     prev : function(e)
36992     {
36993         e.preventDefault();
36994         
36995         this.indicator = Math.max(1, this.indicator - 1);
36996         
36997         this.update();
36998     },
36999     
37000     next : function(e)
37001     {
37002         e.preventDefault();
37003         
37004         this.indicator = Math.min(this.files.length, this.indicator + 1);
37005         
37006         this.update();
37007     }
37008 });
37009 /*
37010  * - LGPL
37011  *
37012  * RadioSet
37013  *
37014  *
37015  */
37016
37017 /**
37018  * @class Roo.bootstrap.form.RadioSet
37019  * @extends Roo.bootstrap.form.Input
37020  * @children Roo.bootstrap.form.Radio
37021  * Bootstrap RadioSet class
37022  * @cfg {String} indicatorpos (left|right) default left
37023  * @cfg {Boolean} inline (true|false) inline the element (default true)
37024  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37025  * @constructor
37026  * Create a new RadioSet
37027  * @param {Object} config The config object
37028  */
37029
37030 Roo.bootstrap.form.RadioSet = function(config){
37031     
37032     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37033     
37034     this.radioes = [];
37035     
37036     Roo.bootstrap.form.RadioSet.register(this);
37037     
37038     this.addEvents({
37039         /**
37040         * @event check
37041         * Fires when the element is checked or unchecked.
37042         * @param {Roo.bootstrap.form.RadioSet} this This radio
37043         * @param {Roo.bootstrap.form.Radio} item The checked item
37044         */
37045        check : true,
37046        /**
37047         * @event click
37048         * Fires when the element is click.
37049         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37050         * @param {Roo.bootstrap.form.Radio} item The checked item
37051         * @param {Roo.EventObject} e The event object
37052         */
37053        click : true
37054     });
37055     
37056 };
37057
37058 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37059
37060     radioes : false,
37061     
37062     inline : true,
37063     
37064     weight : '',
37065     
37066     indicatorpos : 'left',
37067     
37068     getAutoCreate : function()
37069     {
37070         var label = {
37071             tag : 'label',
37072             cls : 'roo-radio-set-label',
37073             cn : [
37074                 {
37075                     tag : 'span',
37076                     html : this.fieldLabel
37077                 }
37078             ]
37079         };
37080         if (Roo.bootstrap.version == 3) {
37081             
37082             
37083             if(this.indicatorpos == 'left'){
37084                 label.cn.unshift({
37085                     tag : 'i',
37086                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37087                     tooltip : 'This field is required'
37088                 });
37089             } else {
37090                 label.cn.push({
37091                     tag : 'i',
37092                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37093                     tooltip : 'This field is required'
37094                 });
37095             }
37096         }
37097         var items = {
37098             tag : 'div',
37099             cls : 'roo-radio-set-items'
37100         };
37101         
37102         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37103         
37104         if (align === 'left' && this.fieldLabel.length) {
37105             
37106             items = {
37107                 cls : "roo-radio-set-right", 
37108                 cn: [
37109                     items
37110                 ]
37111             };
37112             
37113             if(this.labelWidth > 12){
37114                 label.style = "width: " + this.labelWidth + 'px';
37115             }
37116             
37117             if(this.labelWidth < 13 && this.labelmd == 0){
37118                 this.labelmd = this.labelWidth;
37119             }
37120             
37121             if(this.labellg > 0){
37122                 label.cls += ' col-lg-' + this.labellg;
37123                 items.cls += ' col-lg-' + (12 - this.labellg);
37124             }
37125             
37126             if(this.labelmd > 0){
37127                 label.cls += ' col-md-' + this.labelmd;
37128                 items.cls += ' col-md-' + (12 - this.labelmd);
37129             }
37130             
37131             if(this.labelsm > 0){
37132                 label.cls += ' col-sm-' + this.labelsm;
37133                 items.cls += ' col-sm-' + (12 - this.labelsm);
37134             }
37135             
37136             if(this.labelxs > 0){
37137                 label.cls += ' col-xs-' + this.labelxs;
37138                 items.cls += ' col-xs-' + (12 - this.labelxs);
37139             }
37140         }
37141         
37142         var cfg = {
37143             tag : 'div',
37144             cls : 'roo-radio-set',
37145             cn : [
37146                 {
37147                     tag : 'input',
37148                     cls : 'roo-radio-set-input',
37149                     type : 'hidden',
37150                     name : this.name,
37151                     value : this.value ? this.value :  ''
37152                 },
37153                 label,
37154                 items
37155             ]
37156         };
37157         
37158         if(this.weight.length){
37159             cfg.cls += ' roo-radio-' + this.weight;
37160         }
37161         
37162         if(this.inline) {
37163             cfg.cls += ' roo-radio-set-inline';
37164         }
37165         
37166         var settings=this;
37167         ['xs','sm','md','lg'].map(function(size){
37168             if (settings[size]) {
37169                 cfg.cls += ' col-' + size + '-' + settings[size];
37170             }
37171         });
37172         
37173         return cfg;
37174         
37175     },
37176
37177     initEvents : function()
37178     {
37179         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37180         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37181         
37182         if(!this.fieldLabel.length){
37183             this.labelEl.hide();
37184         }
37185         
37186         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37187         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37188         
37189         this.indicator = this.indicatorEl();
37190         
37191         if(this.indicator){
37192             this.indicator.addClass('invisible');
37193         }
37194         
37195         this.originalValue = this.getValue();
37196         
37197     },
37198     
37199     inputEl: function ()
37200     {
37201         return this.el.select('.roo-radio-set-input', true).first();
37202     },
37203     
37204     getChildContainer : function()
37205     {
37206         return this.itemsEl;
37207     },
37208     
37209     register : function(item)
37210     {
37211         this.radioes.push(item);
37212         
37213     },
37214     
37215     validate : function()
37216     {   
37217         if(this.getVisibilityEl().hasClass('hidden')){
37218             return true;
37219         }
37220         
37221         var valid = false;
37222         
37223         Roo.each(this.radioes, function(i){
37224             if(!i.checked){
37225                 return;
37226             }
37227             
37228             valid = true;
37229             return false;
37230         });
37231         
37232         if(this.allowBlank) {
37233             return true;
37234         }
37235         
37236         if(this.disabled || valid){
37237             this.markValid();
37238             return true;
37239         }
37240         
37241         this.markInvalid();
37242         return false;
37243         
37244     },
37245     
37246     markValid : function()
37247     {
37248         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37249             this.indicatorEl().removeClass('visible');
37250             this.indicatorEl().addClass('invisible');
37251         }
37252         
37253         
37254         if (Roo.bootstrap.version == 3) {
37255             this.el.removeClass([this.invalidClass, this.validClass]);
37256             this.el.addClass(this.validClass);
37257         } else {
37258             this.el.removeClass(['is-invalid','is-valid']);
37259             this.el.addClass(['is-valid']);
37260         }
37261         this.fireEvent('valid', this);
37262     },
37263     
37264     markInvalid : function(msg)
37265     {
37266         if(this.allowBlank || this.disabled){
37267             return;
37268         }
37269         
37270         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37271             this.indicatorEl().removeClass('invisible');
37272             this.indicatorEl().addClass('visible');
37273         }
37274         if (Roo.bootstrap.version == 3) {
37275             this.el.removeClass([this.invalidClass, this.validClass]);
37276             this.el.addClass(this.invalidClass);
37277         } else {
37278             this.el.removeClass(['is-invalid','is-valid']);
37279             this.el.addClass(['is-invalid']);
37280         }
37281         
37282         this.fireEvent('invalid', this, msg);
37283         
37284     },
37285     
37286     setValue : function(v, suppressEvent)
37287     {   
37288         if(this.value === v){
37289             return;
37290         }
37291         
37292         this.value = v;
37293         
37294         if(this.rendered){
37295             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37296         }
37297         
37298         Roo.each(this.radioes, function(i){
37299             i.checked = false;
37300             i.el.removeClass('checked');
37301         });
37302         
37303         Roo.each(this.radioes, function(i){
37304             
37305             if(i.value === v || i.value.toString() === v.toString()){
37306                 i.checked = true;
37307                 i.el.addClass('checked');
37308                 
37309                 if(suppressEvent !== true){
37310                     this.fireEvent('check', this, i);
37311                 }
37312                 
37313                 return false;
37314             }
37315             
37316         }, this);
37317         
37318         this.validate();
37319     },
37320     
37321     clearInvalid : function(){
37322         
37323         if(!this.el || this.preventMark){
37324             return;
37325         }
37326         
37327         this.el.removeClass([this.invalidClass]);
37328         
37329         this.fireEvent('valid', this);
37330     }
37331     
37332 });
37333
37334 Roo.apply(Roo.bootstrap.form.RadioSet, {
37335     
37336     groups: {},
37337     
37338     register : function(set)
37339     {
37340         this.groups[set.name] = set;
37341     },
37342     
37343     get: function(name) 
37344     {
37345         if (typeof(this.groups[name]) == 'undefined') {
37346             return false;
37347         }
37348         
37349         return this.groups[name] ;
37350     }
37351     
37352 });
37353 /*
37354  * Based on:
37355  * Ext JS Library 1.1.1
37356  * Copyright(c) 2006-2007, Ext JS, LLC.
37357  *
37358  * Originally Released Under LGPL - original licence link has changed is not relivant.
37359  *
37360  * Fork - LGPL
37361  * <script type="text/javascript">
37362  */
37363
37364
37365 /**
37366  * @class Roo.bootstrap.SplitBar
37367  * @extends Roo.util.Observable
37368  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37369  * <br><br>
37370  * Usage:
37371  * <pre><code>
37372 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37373                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37374 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37375 split.minSize = 100;
37376 split.maxSize = 600;
37377 split.animate = true;
37378 split.on('moved', splitterMoved);
37379 </code></pre>
37380  * @constructor
37381  * Create a new SplitBar
37382  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37383  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37384  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37385  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37386                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37387                         position of the SplitBar).
37388  */
37389 Roo.bootstrap.SplitBar = function(cfg){
37390     
37391     /** @private */
37392     
37393     //{
37394     //  dragElement : elm
37395     //  resizingElement: el,
37396         // optional..
37397     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37398     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37399         // existingProxy ???
37400     //}
37401     
37402     this.el = Roo.get(cfg.dragElement, true);
37403     this.el.dom.unselectable = "on";
37404     /** @private */
37405     this.resizingEl = Roo.get(cfg.resizingElement, true);
37406
37407     /**
37408      * @private
37409      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37410      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37411      * @type Number
37412      */
37413     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37414     
37415     /**
37416      * The minimum size of the resizing element. (Defaults to 0)
37417      * @type Number
37418      */
37419     this.minSize = 0;
37420     
37421     /**
37422      * The maximum size of the resizing element. (Defaults to 2000)
37423      * @type Number
37424      */
37425     this.maxSize = 2000;
37426     
37427     /**
37428      * Whether to animate the transition to the new size
37429      * @type Boolean
37430      */
37431     this.animate = false;
37432     
37433     /**
37434      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37435      * @type Boolean
37436      */
37437     this.useShim = false;
37438     
37439     /** @private */
37440     this.shim = null;
37441     
37442     if(!cfg.existingProxy){
37443         /** @private */
37444         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37445     }else{
37446         this.proxy = Roo.get(cfg.existingProxy).dom;
37447     }
37448     /** @private */
37449     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37450     
37451     /** @private */
37452     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37453     
37454     /** @private */
37455     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37456     
37457     /** @private */
37458     this.dragSpecs = {};
37459     
37460     /**
37461      * @private The adapter to use to positon and resize elements
37462      */
37463     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37464     this.adapter.init(this);
37465     
37466     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37467         /** @private */
37468         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37469         this.el.addClass("roo-splitbar-h");
37470     }else{
37471         /** @private */
37472         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37473         this.el.addClass("roo-splitbar-v");
37474     }
37475     
37476     this.addEvents({
37477         /**
37478          * @event resize
37479          * Fires when the splitter is moved (alias for {@link #event-moved})
37480          * @param {Roo.bootstrap.SplitBar} this
37481          * @param {Number} newSize the new width or height
37482          */
37483         "resize" : true,
37484         /**
37485          * @event moved
37486          * Fires when the splitter is moved
37487          * @param {Roo.bootstrap.SplitBar} this
37488          * @param {Number} newSize the new width or height
37489          */
37490         "moved" : true,
37491         /**
37492          * @event beforeresize
37493          * Fires before the splitter is dragged
37494          * @param {Roo.bootstrap.SplitBar} this
37495          */
37496         "beforeresize" : true,
37497
37498         "beforeapply" : true
37499     });
37500
37501     Roo.util.Observable.call(this);
37502 };
37503
37504 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37505     onStartProxyDrag : function(x, y){
37506         this.fireEvent("beforeresize", this);
37507         if(!this.overlay){
37508             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37509             o.unselectable();
37510             o.enableDisplayMode("block");
37511             // all splitbars share the same overlay
37512             Roo.bootstrap.SplitBar.prototype.overlay = o;
37513         }
37514         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37515         this.overlay.show();
37516         Roo.get(this.proxy).setDisplayed("block");
37517         var size = this.adapter.getElementSize(this);
37518         this.activeMinSize = this.getMinimumSize();;
37519         this.activeMaxSize = this.getMaximumSize();;
37520         var c1 = size - this.activeMinSize;
37521         var c2 = Math.max(this.activeMaxSize - size, 0);
37522         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37523             this.dd.resetConstraints();
37524             this.dd.setXConstraint(
37525                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37526                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37527             );
37528             this.dd.setYConstraint(0, 0);
37529         }else{
37530             this.dd.resetConstraints();
37531             this.dd.setXConstraint(0, 0);
37532             this.dd.setYConstraint(
37533                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37534                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37535             );
37536          }
37537         this.dragSpecs.startSize = size;
37538         this.dragSpecs.startPoint = [x, y];
37539         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37540     },
37541     
37542     /** 
37543      * @private Called after the drag operation by the DDProxy
37544      */
37545     onEndProxyDrag : function(e){
37546         Roo.get(this.proxy).setDisplayed(false);
37547         var endPoint = Roo.lib.Event.getXY(e);
37548         if(this.overlay){
37549             this.overlay.hide();
37550         }
37551         var newSize;
37552         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37553             newSize = this.dragSpecs.startSize + 
37554                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37555                     endPoint[0] - this.dragSpecs.startPoint[0] :
37556                     this.dragSpecs.startPoint[0] - endPoint[0]
37557                 );
37558         }else{
37559             newSize = this.dragSpecs.startSize + 
37560                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37561                     endPoint[1] - this.dragSpecs.startPoint[1] :
37562                     this.dragSpecs.startPoint[1] - endPoint[1]
37563                 );
37564         }
37565         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37566         if(newSize != this.dragSpecs.startSize){
37567             if(this.fireEvent('beforeapply', this, newSize) !== false){
37568                 this.adapter.setElementSize(this, newSize);
37569                 this.fireEvent("moved", this, newSize);
37570                 this.fireEvent("resize", this, newSize);
37571             }
37572         }
37573     },
37574     
37575     /**
37576      * Get the adapter this SplitBar uses
37577      * @return The adapter object
37578      */
37579     getAdapter : function(){
37580         return this.adapter;
37581     },
37582     
37583     /**
37584      * Set the adapter this SplitBar uses
37585      * @param {Object} adapter A SplitBar adapter object
37586      */
37587     setAdapter : function(adapter){
37588         this.adapter = adapter;
37589         this.adapter.init(this);
37590     },
37591     
37592     /**
37593      * Gets the minimum size for the resizing element
37594      * @return {Number} The minimum size
37595      */
37596     getMinimumSize : function(){
37597         return this.minSize;
37598     },
37599     
37600     /**
37601      * Sets the minimum size for the resizing element
37602      * @param {Number} minSize The minimum size
37603      */
37604     setMinimumSize : function(minSize){
37605         this.minSize = minSize;
37606     },
37607     
37608     /**
37609      * Gets the maximum size for the resizing element
37610      * @return {Number} The maximum size
37611      */
37612     getMaximumSize : function(){
37613         return this.maxSize;
37614     },
37615     
37616     /**
37617      * Sets the maximum size for the resizing element
37618      * @param {Number} maxSize The maximum size
37619      */
37620     setMaximumSize : function(maxSize){
37621         this.maxSize = maxSize;
37622     },
37623     
37624     /**
37625      * Sets the initialize size for the resizing element
37626      * @param {Number} size The initial size
37627      */
37628     setCurrentSize : function(size){
37629         var oldAnimate = this.animate;
37630         this.animate = false;
37631         this.adapter.setElementSize(this, size);
37632         this.animate = oldAnimate;
37633     },
37634     
37635     /**
37636      * Destroy this splitbar. 
37637      * @param {Boolean} removeEl True to remove the element
37638      */
37639     destroy : function(removeEl){
37640         if(this.shim){
37641             this.shim.remove();
37642         }
37643         this.dd.unreg();
37644         this.proxy.parentNode.removeChild(this.proxy);
37645         if(removeEl){
37646             this.el.remove();
37647         }
37648     }
37649 });
37650
37651 /**
37652  * @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.
37653  */
37654 Roo.bootstrap.SplitBar.createProxy = function(dir){
37655     var proxy = new Roo.Element(document.createElement("div"));
37656     proxy.unselectable();
37657     var cls = 'roo-splitbar-proxy';
37658     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37659     document.body.appendChild(proxy.dom);
37660     return proxy.dom;
37661 };
37662
37663 /** 
37664  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37665  * Default Adapter. It assumes the splitter and resizing element are not positioned
37666  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37667  */
37668 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37669 };
37670
37671 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37672     // do nothing for now
37673     init : function(s){
37674     
37675     },
37676     /**
37677      * Called before drag operations to get the current size of the resizing element. 
37678      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37679      */
37680      getElementSize : function(s){
37681         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37682             return s.resizingEl.getWidth();
37683         }else{
37684             return s.resizingEl.getHeight();
37685         }
37686     },
37687     
37688     /**
37689      * Called after drag operations to set the size of the resizing element.
37690      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37691      * @param {Number} newSize The new size to set
37692      * @param {Function} onComplete A function to be invoked when resizing is complete
37693      */
37694     setElementSize : function(s, newSize, onComplete){
37695         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37696             if(!s.animate){
37697                 s.resizingEl.setWidth(newSize);
37698                 if(onComplete){
37699                     onComplete(s, newSize);
37700                 }
37701             }else{
37702                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37703             }
37704         }else{
37705             
37706             if(!s.animate){
37707                 s.resizingEl.setHeight(newSize);
37708                 if(onComplete){
37709                     onComplete(s, newSize);
37710                 }
37711             }else{
37712                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37713             }
37714         }
37715     }
37716 };
37717
37718 /** 
37719  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37720  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37721  * Adapter that  moves the splitter element to align with the resized sizing element. 
37722  * Used with an absolute positioned SplitBar.
37723  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37724  * document.body, make sure you assign an id to the body element.
37725  */
37726 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37727     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37728     this.container = Roo.get(container);
37729 };
37730
37731 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37732     init : function(s){
37733         this.basic.init(s);
37734     },
37735     
37736     getElementSize : function(s){
37737         return this.basic.getElementSize(s);
37738     },
37739     
37740     setElementSize : function(s, newSize, onComplete){
37741         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37742     },
37743     
37744     moveSplitter : function(s){
37745         var yes = Roo.bootstrap.SplitBar;
37746         switch(s.placement){
37747             case yes.LEFT:
37748                 s.el.setX(s.resizingEl.getRight());
37749                 break;
37750             case yes.RIGHT:
37751                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37752                 break;
37753             case yes.TOP:
37754                 s.el.setY(s.resizingEl.getBottom());
37755                 break;
37756             case yes.BOTTOM:
37757                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37758                 break;
37759         }
37760     }
37761 };
37762
37763 /**
37764  * Orientation constant - Create a vertical SplitBar
37765  * @static
37766  * @type Number
37767  */
37768 Roo.bootstrap.SplitBar.VERTICAL = 1;
37769
37770 /**
37771  * Orientation constant - Create a horizontal SplitBar
37772  * @static
37773  * @type Number
37774  */
37775 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37776
37777 /**
37778  * Placement constant - The resizing element is to the left of the splitter element
37779  * @static
37780  * @type Number
37781  */
37782 Roo.bootstrap.SplitBar.LEFT = 1;
37783
37784 /**
37785  * Placement constant - The resizing element is to the right of the splitter element
37786  * @static
37787  * @type Number
37788  */
37789 Roo.bootstrap.SplitBar.RIGHT = 2;
37790
37791 /**
37792  * Placement constant - The resizing element is positioned above the splitter element
37793  * @static
37794  * @type Number
37795  */
37796 Roo.bootstrap.SplitBar.TOP = 3;
37797
37798 /**
37799  * Placement constant - The resizing element is positioned under splitter element
37800  * @static
37801  * @type Number
37802  */
37803 Roo.bootstrap.SplitBar.BOTTOM = 4;
37804 /*
37805  * Based on:
37806  * Ext JS Library 1.1.1
37807  * Copyright(c) 2006-2007, Ext JS, LLC.
37808  *
37809  * Originally Released Under LGPL - original licence link has changed is not relivant.
37810  *
37811  * Fork - LGPL
37812  * <script type="text/javascript">
37813  */
37814
37815 /**
37816  * @class Roo.bootstrap.layout.Manager
37817  * @extends Roo.bootstrap.Component
37818  * Base class for layout managers.
37819  */
37820 Roo.bootstrap.layout.Manager = function(config)
37821 {
37822     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37823
37824
37825
37826
37827
37828     /** false to disable window resize monitoring @type Boolean */
37829     this.monitorWindowResize = true;
37830     this.regions = {};
37831     this.addEvents({
37832         /**
37833          * @event layout
37834          * Fires when a layout is performed.
37835          * @param {Roo.LayoutManager} this
37836          */
37837         "layout" : true,
37838         /**
37839          * @event regionresized
37840          * Fires when the user resizes a region.
37841          * @param {Roo.LayoutRegion} region The resized region
37842          * @param {Number} newSize The new size (width for east/west, height for north/south)
37843          */
37844         "regionresized" : true,
37845         /**
37846          * @event regioncollapsed
37847          * Fires when a region is collapsed.
37848          * @param {Roo.LayoutRegion} region The collapsed region
37849          */
37850         "regioncollapsed" : true,
37851         /**
37852          * @event regionexpanded
37853          * Fires when a region is expanded.
37854          * @param {Roo.LayoutRegion} region The expanded region
37855          */
37856         "regionexpanded" : true
37857     });
37858     this.updating = false;
37859
37860     if (config.el) {
37861         this.el = Roo.get(config.el);
37862         this.initEvents();
37863     }
37864
37865 };
37866
37867 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37868
37869
37870     regions : null,
37871
37872     monitorWindowResize : true,
37873
37874
37875     updating : false,
37876
37877
37878     onRender : function(ct, position)
37879     {
37880         if(!this.el){
37881             this.el = Roo.get(ct);
37882             this.initEvents();
37883         }
37884         //this.fireEvent('render',this);
37885     },
37886
37887
37888     initEvents: function()
37889     {
37890
37891
37892         // ie scrollbar fix
37893         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37894             document.body.scroll = "no";
37895         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37896             this.el.position('relative');
37897         }
37898         this.id = this.el.id;
37899         this.el.addClass("roo-layout-container");
37900         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37901         if(this.el.dom != document.body ) {
37902             this.el.on('resize', this.layout,this);
37903             this.el.on('show', this.layout,this);
37904         }
37905
37906     },
37907
37908     /**
37909      * Returns true if this layout is currently being updated
37910      * @return {Boolean}
37911      */
37912     isUpdating : function(){
37913         return this.updating;
37914     },
37915
37916     /**
37917      * Suspend the LayoutManager from doing auto-layouts while
37918      * making multiple add or remove calls
37919      */
37920     beginUpdate : function(){
37921         this.updating = true;
37922     },
37923
37924     /**
37925      * Restore auto-layouts and optionally disable the manager from performing a layout
37926      * @param {Boolean} noLayout true to disable a layout update
37927      */
37928     endUpdate : function(noLayout){
37929         this.updating = false;
37930         if(!noLayout){
37931             this.layout();
37932         }
37933     },
37934
37935     layout: function(){
37936         // abstract...
37937     },
37938
37939     onRegionResized : function(region, newSize){
37940         this.fireEvent("regionresized", region, newSize);
37941         this.layout();
37942     },
37943
37944     onRegionCollapsed : function(region){
37945         this.fireEvent("regioncollapsed", region);
37946     },
37947
37948     onRegionExpanded : function(region){
37949         this.fireEvent("regionexpanded", region);
37950     },
37951
37952     /**
37953      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37954      * performs box-model adjustments.
37955      * @return {Object} The size as an object {width: (the width), height: (the height)}
37956      */
37957     getViewSize : function()
37958     {
37959         var size;
37960         if(this.el.dom != document.body){
37961             size = this.el.getSize();
37962         }else{
37963             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37964         }
37965         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37966         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37967         return size;
37968     },
37969
37970     /**
37971      * Returns the Element this layout is bound to.
37972      * @return {Roo.Element}
37973      */
37974     getEl : function(){
37975         return this.el;
37976     },
37977
37978     /**
37979      * Returns the specified region.
37980      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37981      * @return {Roo.LayoutRegion}
37982      */
37983     getRegion : function(target){
37984         return this.regions[target.toLowerCase()];
37985     },
37986
37987     onWindowResize : function(){
37988         if(this.monitorWindowResize){
37989             this.layout();
37990         }
37991     }
37992 });
37993 /*
37994  * Based on:
37995  * Ext JS Library 1.1.1
37996  * Copyright(c) 2006-2007, Ext JS, LLC.
37997  *
37998  * Originally Released Under LGPL - original licence link has changed is not relivant.
37999  *
38000  * Fork - LGPL
38001  * <script type="text/javascript">
38002  */
38003 /**
38004  * @class Roo.bootstrap.layout.Border
38005  * @extends Roo.bootstrap.layout.Manager
38006  * @builder-top
38007  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38008  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38009  * please see: examples/bootstrap/nested.html<br><br>
38010  
38011 <b>The container the layout is rendered into can be either the body element or any other element.
38012 If it is not the body element, the container needs to either be an absolute positioned element,
38013 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38014 the container size if it is not the body element.</b>
38015
38016 * @constructor
38017 * Create a new Border
38018 * @param {Object} config Configuration options
38019  */
38020 Roo.bootstrap.layout.Border = function(config){
38021     config = config || {};
38022     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38023     
38024     
38025     
38026     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38027         if(config[region]){
38028             config[region].region = region;
38029             this.addRegion(config[region]);
38030         }
38031     },this);
38032     
38033 };
38034
38035 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38036
38037 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38038     
38039         /**
38040          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38041          */
38042         /**
38043          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38044          */
38045         /**
38046          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38047          */
38048         /**
38049          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38050          */
38051         /**
38052          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38053          */
38054         
38055         
38056         
38057         
38058     parent : false, // this might point to a 'nest' or a ???
38059     
38060     /**
38061      * Creates and adds a new region if it doesn't already exist.
38062      * @param {String} target The target region key (north, south, east, west or center).
38063      * @param {Object} config The regions config object
38064      * @return {BorderLayoutRegion} The new region
38065      */
38066     addRegion : function(config)
38067     {
38068         if(!this.regions[config.region]){
38069             var r = this.factory(config);
38070             this.bindRegion(r);
38071         }
38072         return this.regions[config.region];
38073     },
38074
38075     // private (kinda)
38076     bindRegion : function(r){
38077         this.regions[r.config.region] = r;
38078         
38079         r.on("visibilitychange",    this.layout, this);
38080         r.on("paneladded",          this.layout, this);
38081         r.on("panelremoved",        this.layout, this);
38082         r.on("invalidated",         this.layout, this);
38083         r.on("resized",             this.onRegionResized, this);
38084         r.on("collapsed",           this.onRegionCollapsed, this);
38085         r.on("expanded",            this.onRegionExpanded, this);
38086     },
38087
38088     /**
38089      * Performs a layout update.
38090      */
38091     layout : function()
38092     {
38093         if(this.updating) {
38094             return;
38095         }
38096         
38097         // render all the rebions if they have not been done alreayd?
38098         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38099             if(this.regions[region] && !this.regions[region].bodyEl){
38100                 this.regions[region].onRender(this.el)
38101             }
38102         },this);
38103         
38104         var size = this.getViewSize();
38105         var w = size.width;
38106         var h = size.height;
38107         var centerW = w;
38108         var centerH = h;
38109         var centerY = 0;
38110         var centerX = 0;
38111         //var x = 0, y = 0;
38112
38113         var rs = this.regions;
38114         var north = rs["north"];
38115         var south = rs["south"]; 
38116         var west = rs["west"];
38117         var east = rs["east"];
38118         var center = rs["center"];
38119         //if(this.hideOnLayout){ // not supported anymore
38120             //c.el.setStyle("display", "none");
38121         //}
38122         if(north && north.isVisible()){
38123             var b = north.getBox();
38124             var m = north.getMargins();
38125             b.width = w - (m.left+m.right);
38126             b.x = m.left;
38127             b.y = m.top;
38128             centerY = b.height + b.y + m.bottom;
38129             centerH -= centerY;
38130             north.updateBox(this.safeBox(b));
38131         }
38132         if(south && south.isVisible()){
38133             var b = south.getBox();
38134             var m = south.getMargins();
38135             b.width = w - (m.left+m.right);
38136             b.x = m.left;
38137             var totalHeight = (b.height + m.top + m.bottom);
38138             b.y = h - totalHeight + m.top;
38139             centerH -= totalHeight;
38140             south.updateBox(this.safeBox(b));
38141         }
38142         if(west && west.isVisible()){
38143             var b = west.getBox();
38144             var m = west.getMargins();
38145             b.height = centerH - (m.top+m.bottom);
38146             b.x = m.left;
38147             b.y = centerY + m.top;
38148             var totalWidth = (b.width + m.left + m.right);
38149             centerX += totalWidth;
38150             centerW -= totalWidth;
38151             west.updateBox(this.safeBox(b));
38152         }
38153         if(east && east.isVisible()){
38154             var b = east.getBox();
38155             var m = east.getMargins();
38156             b.height = centerH - (m.top+m.bottom);
38157             var totalWidth = (b.width + m.left + m.right);
38158             b.x = w - totalWidth + m.left;
38159             b.y = centerY + m.top;
38160             centerW -= totalWidth;
38161             east.updateBox(this.safeBox(b));
38162         }
38163         if(center){
38164             var m = center.getMargins();
38165             var centerBox = {
38166                 x: centerX + m.left,
38167                 y: centerY + m.top,
38168                 width: centerW - (m.left+m.right),
38169                 height: centerH - (m.top+m.bottom)
38170             };
38171             //if(this.hideOnLayout){
38172                 //center.el.setStyle("display", "block");
38173             //}
38174             center.updateBox(this.safeBox(centerBox));
38175         }
38176         this.el.repaint();
38177         this.fireEvent("layout", this);
38178     },
38179
38180     // private
38181     safeBox : function(box){
38182         box.width = Math.max(0, box.width);
38183         box.height = Math.max(0, box.height);
38184         return box;
38185     },
38186
38187     /**
38188      * Adds a ContentPanel (or subclass) to this layout.
38189      * @param {String} target The target region key (north, south, east, west or center).
38190      * @param {Roo.ContentPanel} panel The panel to add
38191      * @return {Roo.ContentPanel} The added panel
38192      */
38193     add : function(target, panel){
38194          
38195         target = target.toLowerCase();
38196         return this.regions[target].add(panel);
38197     },
38198
38199     /**
38200      * Remove a ContentPanel (or subclass) to this layout.
38201      * @param {String} target The target region key (north, south, east, west or center).
38202      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38203      * @return {Roo.ContentPanel} The removed panel
38204      */
38205     remove : function(target, panel){
38206         target = target.toLowerCase();
38207         return this.regions[target].remove(panel);
38208     },
38209
38210     /**
38211      * Searches all regions for a panel with the specified id
38212      * @param {String} panelId
38213      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38214      */
38215     findPanel : function(panelId){
38216         var rs = this.regions;
38217         for(var target in rs){
38218             if(typeof rs[target] != "function"){
38219                 var p = rs[target].getPanel(panelId);
38220                 if(p){
38221                     return p;
38222                 }
38223             }
38224         }
38225         return null;
38226     },
38227
38228     /**
38229      * Searches all regions for a panel with the specified id and activates (shows) it.
38230      * @param {String/ContentPanel} panelId The panels id or the panel itself
38231      * @return {Roo.ContentPanel} The shown panel or null
38232      */
38233     showPanel : function(panelId) {
38234       var rs = this.regions;
38235       for(var target in rs){
38236          var r = rs[target];
38237          if(typeof r != "function"){
38238             if(r.hasPanel(panelId)){
38239                return r.showPanel(panelId);
38240             }
38241          }
38242       }
38243       return null;
38244    },
38245
38246    /**
38247      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38248      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38249      */
38250    /*
38251     restoreState : function(provider){
38252         if(!provider){
38253             provider = Roo.state.Manager;
38254         }
38255         var sm = new Roo.LayoutStateManager();
38256         sm.init(this, provider);
38257     },
38258 */
38259  
38260  
38261     /**
38262      * Adds a xtype elements to the layout.
38263      * <pre><code>
38264
38265 layout.addxtype({
38266        xtype : 'ContentPanel',
38267        region: 'west',
38268        items: [ .... ]
38269    }
38270 );
38271
38272 layout.addxtype({
38273         xtype : 'NestedLayoutPanel',
38274         region: 'west',
38275         layout: {
38276            center: { },
38277            west: { }   
38278         },
38279         items : [ ... list of content panels or nested layout panels.. ]
38280    }
38281 );
38282 </code></pre>
38283      * @param {Object} cfg Xtype definition of item to add.
38284      */
38285     addxtype : function(cfg)
38286     {
38287         // basically accepts a pannel...
38288         // can accept a layout region..!?!?
38289         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38290         
38291         
38292         // theory?  children can only be panels??
38293         
38294         //if (!cfg.xtype.match(/Panel$/)) {
38295         //    return false;
38296         //}
38297         var ret = false;
38298         
38299         if (typeof(cfg.region) == 'undefined') {
38300             Roo.log("Failed to add Panel, region was not set");
38301             Roo.log(cfg);
38302             return false;
38303         }
38304         var region = cfg.region;
38305         delete cfg.region;
38306         
38307           
38308         var xitems = [];
38309         if (cfg.items) {
38310             xitems = cfg.items;
38311             delete cfg.items;
38312         }
38313         var nb = false;
38314         
38315         if ( region == 'center') {
38316             Roo.log("Center: " + cfg.title);
38317         }
38318         
38319         
38320         switch(cfg.xtype) 
38321         {
38322             case 'Content':  // ContentPanel (el, cfg)
38323             case 'Scroll':  // ContentPanel (el, cfg)
38324             case 'View': 
38325                 cfg.autoCreate = cfg.autoCreate || true;
38326                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38327                 //} else {
38328                 //    var el = this.el.createChild();
38329                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38330                 //}
38331                 
38332                 this.add(region, ret);
38333                 break;
38334             
38335             /*
38336             case 'TreePanel': // our new panel!
38337                 cfg.el = this.el.createChild();
38338                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38339                 this.add(region, ret);
38340                 break;
38341             */
38342             
38343             case 'Nest': 
38344                 // create a new Layout (which is  a Border Layout...
38345                 
38346                 var clayout = cfg.layout;
38347                 clayout.el  = this.el.createChild();
38348                 clayout.items   = clayout.items  || [];
38349                 
38350                 delete cfg.layout;
38351                 
38352                 // replace this exitems with the clayout ones..
38353                 xitems = clayout.items;
38354                  
38355                 // force background off if it's in center...
38356                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38357                     cfg.background = false;
38358                 }
38359                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38360                 
38361                 
38362                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38363                 //console.log('adding nested layout panel '  + cfg.toSource());
38364                 this.add(region, ret);
38365                 nb = {}; /// find first...
38366                 break;
38367             
38368             case 'Grid':
38369                 
38370                 // needs grid and region
38371                 
38372                 //var el = this.getRegion(region).el.createChild();
38373                 /*
38374                  *var el = this.el.createChild();
38375                 // create the grid first...
38376                 cfg.grid.container = el;
38377                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38378                 */
38379                 
38380                 if (region == 'center' && this.active ) {
38381                     cfg.background = false;
38382                 }
38383                 
38384                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38385                 
38386                 this.add(region, ret);
38387                 /*
38388                 if (cfg.background) {
38389                     // render grid on panel activation (if panel background)
38390                     ret.on('activate', function(gp) {
38391                         if (!gp.grid.rendered) {
38392                     //        gp.grid.render(el);
38393                         }
38394                     });
38395                 } else {
38396                   //  cfg.grid.render(el);
38397                 }
38398                 */
38399                 break;
38400            
38401            
38402             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38403                 // it was the old xcomponent building that caused this before.
38404                 // espeically if border is the top element in the tree.
38405                 ret = this;
38406                 break; 
38407                 
38408                     
38409                 
38410                 
38411                 
38412             default:
38413                 /*
38414                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38415                     
38416                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38417                     this.add(region, ret);
38418                 } else {
38419                 */
38420                     Roo.log(cfg);
38421                     throw "Can not add '" + cfg.xtype + "' to Border";
38422                     return null;
38423              
38424                                 
38425              
38426         }
38427         this.beginUpdate();
38428         // add children..
38429         var region = '';
38430         var abn = {};
38431         Roo.each(xitems, function(i)  {
38432             region = nb && i.region ? i.region : false;
38433             
38434             var add = ret.addxtype(i);
38435            
38436             if (region) {
38437                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38438                 if (!i.background) {
38439                     abn[region] = nb[region] ;
38440                 }
38441             }
38442             
38443         });
38444         this.endUpdate();
38445
38446         // make the last non-background panel active..
38447         //if (nb) { Roo.log(abn); }
38448         if (nb) {
38449             
38450             for(var r in abn) {
38451                 region = this.getRegion(r);
38452                 if (region) {
38453                     // tried using nb[r], but it does not work..
38454                      
38455                     region.showPanel(abn[r]);
38456                    
38457                 }
38458             }
38459         }
38460         return ret;
38461         
38462     },
38463     
38464     
38465 // private
38466     factory : function(cfg)
38467     {
38468         
38469         var validRegions = Roo.bootstrap.layout.Border.regions;
38470
38471         var target = cfg.region;
38472         cfg.mgr = this;
38473         
38474         var r = Roo.bootstrap.layout;
38475         Roo.log(target);
38476         switch(target){
38477             case "north":
38478                 return new r.North(cfg);
38479             case "south":
38480                 return new r.South(cfg);
38481             case "east":
38482                 return new r.East(cfg);
38483             case "west":
38484                 return new r.West(cfg);
38485             case "center":
38486                 return new r.Center(cfg);
38487         }
38488         throw 'Layout region "'+target+'" not supported.';
38489     }
38490     
38491     
38492 });
38493  /*
38494  * Based on:
38495  * Ext JS Library 1.1.1
38496  * Copyright(c) 2006-2007, Ext JS, LLC.
38497  *
38498  * Originally Released Under LGPL - original licence link has changed is not relivant.
38499  *
38500  * Fork - LGPL
38501  * <script type="text/javascript">
38502  */
38503  
38504 /**
38505  * @class Roo.bootstrap.layout.Basic
38506  * @extends Roo.util.Observable
38507  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38508  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38509  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38510  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38511  * @cfg {string}   region  the region that it inhabits..
38512  * @cfg {bool}   skipConfig skip config?
38513  * 
38514
38515  */
38516 Roo.bootstrap.layout.Basic = function(config){
38517     
38518     this.mgr = config.mgr;
38519     
38520     this.position = config.region;
38521     
38522     var skipConfig = config.skipConfig;
38523     
38524     this.events = {
38525         /**
38526          * @scope Roo.BasicLayoutRegion
38527          */
38528         
38529         /**
38530          * @event beforeremove
38531          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38532          * @param {Roo.LayoutRegion} this
38533          * @param {Roo.ContentPanel} panel The panel
38534          * @param {Object} e The cancel event object
38535          */
38536         "beforeremove" : true,
38537         /**
38538          * @event invalidated
38539          * Fires when the layout for this region is changed.
38540          * @param {Roo.LayoutRegion} this
38541          */
38542         "invalidated" : true,
38543         /**
38544          * @event visibilitychange
38545          * Fires when this region is shown or hidden 
38546          * @param {Roo.LayoutRegion} this
38547          * @param {Boolean} visibility true or false
38548          */
38549         "visibilitychange" : true,
38550         /**
38551          * @event paneladded
38552          * Fires when a panel is added. 
38553          * @param {Roo.LayoutRegion} this
38554          * @param {Roo.ContentPanel} panel The panel
38555          */
38556         "paneladded" : true,
38557         /**
38558          * @event panelremoved
38559          * Fires when a panel is removed. 
38560          * @param {Roo.LayoutRegion} this
38561          * @param {Roo.ContentPanel} panel The panel
38562          */
38563         "panelremoved" : true,
38564         /**
38565          * @event beforecollapse
38566          * Fires when this region before collapse.
38567          * @param {Roo.LayoutRegion} this
38568          */
38569         "beforecollapse" : true,
38570         /**
38571          * @event collapsed
38572          * Fires when this region is collapsed.
38573          * @param {Roo.LayoutRegion} this
38574          */
38575         "collapsed" : true,
38576         /**
38577          * @event expanded
38578          * Fires when this region is expanded.
38579          * @param {Roo.LayoutRegion} this
38580          */
38581         "expanded" : true,
38582         /**
38583          * @event slideshow
38584          * Fires when this region is slid into view.
38585          * @param {Roo.LayoutRegion} this
38586          */
38587         "slideshow" : true,
38588         /**
38589          * @event slidehide
38590          * Fires when this region slides out of view. 
38591          * @param {Roo.LayoutRegion} this
38592          */
38593         "slidehide" : true,
38594         /**
38595          * @event panelactivated
38596          * Fires when a panel is activated. 
38597          * @param {Roo.LayoutRegion} this
38598          * @param {Roo.ContentPanel} panel The activated panel
38599          */
38600         "panelactivated" : true,
38601         /**
38602          * @event resized
38603          * Fires when the user resizes this region. 
38604          * @param {Roo.LayoutRegion} this
38605          * @param {Number} newSize The new size (width for east/west, height for north/south)
38606          */
38607         "resized" : true
38608     };
38609     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38610     this.panels = new Roo.util.MixedCollection();
38611     this.panels.getKey = this.getPanelId.createDelegate(this);
38612     this.box = null;
38613     this.activePanel = null;
38614     // ensure listeners are added...
38615     
38616     if (config.listeners || config.events) {
38617         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38618             listeners : config.listeners || {},
38619             events : config.events || {}
38620         });
38621     }
38622     
38623     if(skipConfig !== true){
38624         this.applyConfig(config);
38625     }
38626 };
38627
38628 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38629 {
38630     getPanelId : function(p){
38631         return p.getId();
38632     },
38633     
38634     applyConfig : function(config){
38635         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38636         this.config = config;
38637         
38638     },
38639     
38640     /**
38641      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38642      * the width, for horizontal (north, south) the height.
38643      * @param {Number} newSize The new width or height
38644      */
38645     resizeTo : function(newSize){
38646         var el = this.el ? this.el :
38647                  (this.activePanel ? this.activePanel.getEl() : null);
38648         if(el){
38649             switch(this.position){
38650                 case "east":
38651                 case "west":
38652                     el.setWidth(newSize);
38653                     this.fireEvent("resized", this, newSize);
38654                 break;
38655                 case "north":
38656                 case "south":
38657                     el.setHeight(newSize);
38658                     this.fireEvent("resized", this, newSize);
38659                 break;                
38660             }
38661         }
38662     },
38663     
38664     getBox : function(){
38665         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38666     },
38667     
38668     getMargins : function(){
38669         return this.margins;
38670     },
38671     
38672     updateBox : function(box){
38673         this.box = box;
38674         var el = this.activePanel.getEl();
38675         el.dom.style.left = box.x + "px";
38676         el.dom.style.top = box.y + "px";
38677         this.activePanel.setSize(box.width, box.height);
38678     },
38679     
38680     /**
38681      * Returns the container element for this region.
38682      * @return {Roo.Element}
38683      */
38684     getEl : function(){
38685         return this.activePanel;
38686     },
38687     
38688     /**
38689      * Returns true if this region is currently visible.
38690      * @return {Boolean}
38691      */
38692     isVisible : function(){
38693         return this.activePanel ? true : false;
38694     },
38695     
38696     setActivePanel : function(panel){
38697         panel = this.getPanel(panel);
38698         if(this.activePanel && this.activePanel != panel){
38699             this.activePanel.setActiveState(false);
38700             this.activePanel.getEl().setLeftTop(-10000,-10000);
38701         }
38702         this.activePanel = panel;
38703         panel.setActiveState(true);
38704         if(this.box){
38705             panel.setSize(this.box.width, this.box.height);
38706         }
38707         this.fireEvent("panelactivated", this, panel);
38708         this.fireEvent("invalidated");
38709     },
38710     
38711     /**
38712      * Show the specified panel.
38713      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38714      * @return {Roo.ContentPanel} The shown panel or null
38715      */
38716     showPanel : function(panel){
38717         panel = this.getPanel(panel);
38718         if(panel){
38719             this.setActivePanel(panel);
38720         }
38721         return panel;
38722     },
38723     
38724     /**
38725      * Get the active panel for this region.
38726      * @return {Roo.ContentPanel} The active panel or null
38727      */
38728     getActivePanel : function(){
38729         return this.activePanel;
38730     },
38731     
38732     /**
38733      * Add the passed ContentPanel(s)
38734      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38735      * @return {Roo.ContentPanel} The panel added (if only one was added)
38736      */
38737     add : function(panel){
38738         if(arguments.length > 1){
38739             for(var i = 0, len = arguments.length; i < len; i++) {
38740                 this.add(arguments[i]);
38741             }
38742             return null;
38743         }
38744         if(this.hasPanel(panel)){
38745             this.showPanel(panel);
38746             return panel;
38747         }
38748         var el = panel.getEl();
38749         if(el.dom.parentNode != this.mgr.el.dom){
38750             this.mgr.el.dom.appendChild(el.dom);
38751         }
38752         if(panel.setRegion){
38753             panel.setRegion(this);
38754         }
38755         this.panels.add(panel);
38756         el.setStyle("position", "absolute");
38757         if(!panel.background){
38758             this.setActivePanel(panel);
38759             if(this.config.initialSize && this.panels.getCount()==1){
38760                 this.resizeTo(this.config.initialSize);
38761             }
38762         }
38763         this.fireEvent("paneladded", this, panel);
38764         return panel;
38765     },
38766     
38767     /**
38768      * Returns true if the panel is in this region.
38769      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38770      * @return {Boolean}
38771      */
38772     hasPanel : function(panel){
38773         if(typeof panel == "object"){ // must be panel obj
38774             panel = panel.getId();
38775         }
38776         return this.getPanel(panel) ? true : false;
38777     },
38778     
38779     /**
38780      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38781      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38782      * @param {Boolean} preservePanel Overrides the config preservePanel option
38783      * @return {Roo.ContentPanel} The panel that was removed
38784      */
38785     remove : function(panel, preservePanel){
38786         panel = this.getPanel(panel);
38787         if(!panel){
38788             return null;
38789         }
38790         var e = {};
38791         this.fireEvent("beforeremove", this, panel, e);
38792         if(e.cancel === true){
38793             return null;
38794         }
38795         var panelId = panel.getId();
38796         this.panels.removeKey(panelId);
38797         return panel;
38798     },
38799     
38800     /**
38801      * Returns the panel specified or null if it's not in this region.
38802      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38803      * @return {Roo.ContentPanel}
38804      */
38805     getPanel : function(id){
38806         if(typeof id == "object"){ // must be panel obj
38807             return id;
38808         }
38809         return this.panels.get(id);
38810     },
38811     
38812     /**
38813      * Returns this regions position (north/south/east/west/center).
38814      * @return {String} 
38815      */
38816     getPosition: function(){
38817         return this.position;    
38818     }
38819 });/*
38820  * Based on:
38821  * Ext JS Library 1.1.1
38822  * Copyright(c) 2006-2007, Ext JS, LLC.
38823  *
38824  * Originally Released Under LGPL - original licence link has changed is not relivant.
38825  *
38826  * Fork - LGPL
38827  * <script type="text/javascript">
38828  */
38829  
38830 /**
38831  * @class Roo.bootstrap.layout.Region
38832  * @extends Roo.bootstrap.layout.Basic
38833  * This class represents a region in a layout manager.
38834  
38835  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38836  * @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})
38837  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38838  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38839  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38840  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38841  * @cfg {String}    title           The title for the region (overrides panel titles)
38842  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38843  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38844  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38845  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38846  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38847  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38848  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38849  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38850  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38851  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38852
38853  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38854  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38855  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38856  * @cfg {Number}    width           For East/West panels
38857  * @cfg {Number}    height          For North/South panels
38858  * @cfg {Boolean}   split           To show the splitter
38859  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38860  * 
38861  * @cfg {string}   cls             Extra CSS classes to add to region
38862  * 
38863  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38864  * @cfg {string}   region  the region that it inhabits..
38865  *
38866
38867  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38868  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38869
38870  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38871  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38872  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38873  */
38874 Roo.bootstrap.layout.Region = function(config)
38875 {
38876     this.applyConfig(config);
38877
38878     var mgr = config.mgr;
38879     var pos = config.region;
38880     config.skipConfig = true;
38881     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38882     
38883     if (mgr.el) {
38884         this.onRender(mgr.el);   
38885     }
38886      
38887     this.visible = true;
38888     this.collapsed = false;
38889     this.unrendered_panels = [];
38890 };
38891
38892 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38893
38894     position: '', // set by wrapper (eg. north/south etc..)
38895     unrendered_panels : null,  // unrendered panels.
38896     
38897     tabPosition : false,
38898     
38899     mgr: false, // points to 'Border'
38900     
38901     
38902     createBody : function(){
38903         /** This region's body element 
38904         * @type Roo.Element */
38905         this.bodyEl = this.el.createChild({
38906                 tag: "div",
38907                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38908         });
38909     },
38910
38911     onRender: function(ctr, pos)
38912     {
38913         var dh = Roo.DomHelper;
38914         /** This region's container element 
38915         * @type Roo.Element */
38916         this.el = dh.append(ctr.dom, {
38917                 tag: "div",
38918                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38919             }, true);
38920         /** This region's title element 
38921         * @type Roo.Element */
38922     
38923         this.titleEl = dh.append(this.el.dom,  {
38924                 tag: "div",
38925                 unselectable: "on",
38926                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38927                 children:[
38928                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38929                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38930                 ]
38931             }, true);
38932         
38933         this.titleEl.enableDisplayMode();
38934         /** This region's title text element 
38935         * @type HTMLElement */
38936         this.titleTextEl = this.titleEl.dom.firstChild;
38937         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38938         /*
38939         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38940         this.closeBtn.enableDisplayMode();
38941         this.closeBtn.on("click", this.closeClicked, this);
38942         this.closeBtn.hide();
38943     */
38944         this.createBody(this.config);
38945         if(this.config.hideWhenEmpty){
38946             this.hide();
38947             this.on("paneladded", this.validateVisibility, this);
38948             this.on("panelremoved", this.validateVisibility, this);
38949         }
38950         if(this.autoScroll){
38951             this.bodyEl.setStyle("overflow", "auto");
38952         }else{
38953             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38954         }
38955         //if(c.titlebar !== false){
38956             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38957                 this.titleEl.hide();
38958             }else{
38959                 this.titleEl.show();
38960                 if(this.config.title){
38961                     this.titleTextEl.innerHTML = this.config.title;
38962                 }
38963             }
38964         //}
38965         if(this.config.collapsed){
38966             this.collapse(true);
38967         }
38968         if(this.config.hidden){
38969             this.hide();
38970         }
38971         
38972         if (this.unrendered_panels && this.unrendered_panels.length) {
38973             for (var i =0;i< this.unrendered_panels.length; i++) {
38974                 this.add(this.unrendered_panels[i]);
38975             }
38976             this.unrendered_panels = null;
38977             
38978         }
38979         
38980     },
38981     
38982     applyConfig : function(c)
38983     {
38984         /*
38985          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38986             var dh = Roo.DomHelper;
38987             if(c.titlebar !== false){
38988                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38989                 this.collapseBtn.on("click", this.collapse, this);
38990                 this.collapseBtn.enableDisplayMode();
38991                 /*
38992                 if(c.showPin === true || this.showPin){
38993                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38994                     this.stickBtn.enableDisplayMode();
38995                     this.stickBtn.on("click", this.expand, this);
38996                     this.stickBtn.hide();
38997                 }
38998                 
38999             }
39000             */
39001             /** This region's collapsed element
39002             * @type Roo.Element */
39003             /*
39004              *
39005             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39006                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39007             ]}, true);
39008             
39009             if(c.floatable !== false){
39010                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39011                this.collapsedEl.on("click", this.collapseClick, this);
39012             }
39013
39014             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39015                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39016                    id: "message", unselectable: "on", style:{"float":"left"}});
39017                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39018              }
39019             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39020             this.expandBtn.on("click", this.expand, this);
39021             
39022         }
39023         
39024         if(this.collapseBtn){
39025             this.collapseBtn.setVisible(c.collapsible == true);
39026         }
39027         
39028         this.cmargins = c.cmargins || this.cmargins ||
39029                          (this.position == "west" || this.position == "east" ?
39030                              {top: 0, left: 2, right:2, bottom: 0} :
39031                              {top: 2, left: 0, right:0, bottom: 2});
39032         */
39033         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39034         
39035         
39036         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39037         
39038         this.autoScroll = c.autoScroll || false;
39039         
39040         
39041        
39042         
39043         this.duration = c.duration || .30;
39044         this.slideDuration = c.slideDuration || .45;
39045         this.config = c;
39046        
39047     },
39048     /**
39049      * Returns true if this region is currently visible.
39050      * @return {Boolean}
39051      */
39052     isVisible : function(){
39053         return this.visible;
39054     },
39055
39056     /**
39057      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39058      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39059      */
39060     //setCollapsedTitle : function(title){
39061     //    title = title || "&#160;";
39062      //   if(this.collapsedTitleTextEl){
39063       //      this.collapsedTitleTextEl.innerHTML = title;
39064        // }
39065     //},
39066
39067     getBox : function(){
39068         var b;
39069       //  if(!this.collapsed){
39070             b = this.el.getBox(false, true);
39071        // }else{
39072           //  b = this.collapsedEl.getBox(false, true);
39073         //}
39074         return b;
39075     },
39076
39077     getMargins : function(){
39078         return this.margins;
39079         //return this.collapsed ? this.cmargins : this.margins;
39080     },
39081 /*
39082     highlight : function(){
39083         this.el.addClass("x-layout-panel-dragover");
39084     },
39085
39086     unhighlight : function(){
39087         this.el.removeClass("x-layout-panel-dragover");
39088     },
39089 */
39090     updateBox : function(box)
39091     {
39092         if (!this.bodyEl) {
39093             return; // not rendered yet..
39094         }
39095         
39096         this.box = box;
39097         if(!this.collapsed){
39098             this.el.dom.style.left = box.x + "px";
39099             this.el.dom.style.top = box.y + "px";
39100             this.updateBody(box.width, box.height);
39101         }else{
39102             this.collapsedEl.dom.style.left = box.x + "px";
39103             this.collapsedEl.dom.style.top = box.y + "px";
39104             this.collapsedEl.setSize(box.width, box.height);
39105         }
39106         if(this.tabs){
39107             this.tabs.autoSizeTabs();
39108         }
39109     },
39110
39111     updateBody : function(w, h)
39112     {
39113         if(w !== null){
39114             this.el.setWidth(w);
39115             w -= this.el.getBorderWidth("rl");
39116             if(this.config.adjustments){
39117                 w += this.config.adjustments[0];
39118             }
39119         }
39120         if(h !== null && h > 0){
39121             this.el.setHeight(h);
39122             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39123             h -= this.el.getBorderWidth("tb");
39124             if(this.config.adjustments){
39125                 h += this.config.adjustments[1];
39126             }
39127             this.bodyEl.setHeight(h);
39128             if(this.tabs){
39129                 h = this.tabs.syncHeight(h);
39130             }
39131         }
39132         if(this.panelSize){
39133             w = w !== null ? w : this.panelSize.width;
39134             h = h !== null ? h : this.panelSize.height;
39135         }
39136         if(this.activePanel){
39137             var el = this.activePanel.getEl();
39138             w = w !== null ? w : el.getWidth();
39139             h = h !== null ? h : el.getHeight();
39140             this.panelSize = {width: w, height: h};
39141             this.activePanel.setSize(w, h);
39142         }
39143         if(Roo.isIE && this.tabs){
39144             this.tabs.el.repaint();
39145         }
39146     },
39147
39148     /**
39149      * Returns the container element for this region.
39150      * @return {Roo.Element}
39151      */
39152     getEl : function(){
39153         return this.el;
39154     },
39155
39156     /**
39157      * Hides this region.
39158      */
39159     hide : function(){
39160         //if(!this.collapsed){
39161             this.el.dom.style.left = "-2000px";
39162             this.el.hide();
39163         //}else{
39164          //   this.collapsedEl.dom.style.left = "-2000px";
39165          //   this.collapsedEl.hide();
39166        // }
39167         this.visible = false;
39168         this.fireEvent("visibilitychange", this, false);
39169     },
39170
39171     /**
39172      * Shows this region if it was previously hidden.
39173      */
39174     show : function(){
39175         //if(!this.collapsed){
39176             this.el.show();
39177         //}else{
39178         //    this.collapsedEl.show();
39179        // }
39180         this.visible = true;
39181         this.fireEvent("visibilitychange", this, true);
39182     },
39183 /*
39184     closeClicked : function(){
39185         if(this.activePanel){
39186             this.remove(this.activePanel);
39187         }
39188     },
39189
39190     collapseClick : function(e){
39191         if(this.isSlid){
39192            e.stopPropagation();
39193            this.slideIn();
39194         }else{
39195            e.stopPropagation();
39196            this.slideOut();
39197         }
39198     },
39199 */
39200     /**
39201      * Collapses this region.
39202      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39203      */
39204     /*
39205     collapse : function(skipAnim, skipCheck = false){
39206         if(this.collapsed) {
39207             return;
39208         }
39209         
39210         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39211             
39212             this.collapsed = true;
39213             if(this.split){
39214                 this.split.el.hide();
39215             }
39216             if(this.config.animate && skipAnim !== true){
39217                 this.fireEvent("invalidated", this);
39218                 this.animateCollapse();
39219             }else{
39220                 this.el.setLocation(-20000,-20000);
39221                 this.el.hide();
39222                 this.collapsedEl.show();
39223                 this.fireEvent("collapsed", this);
39224                 this.fireEvent("invalidated", this);
39225             }
39226         }
39227         
39228     },
39229 */
39230     animateCollapse : function(){
39231         // overridden
39232     },
39233
39234     /**
39235      * Expands this region if it was previously collapsed.
39236      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39237      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39238      */
39239     /*
39240     expand : function(e, skipAnim){
39241         if(e) {
39242             e.stopPropagation();
39243         }
39244         if(!this.collapsed || this.el.hasActiveFx()) {
39245             return;
39246         }
39247         if(this.isSlid){
39248             this.afterSlideIn();
39249             skipAnim = true;
39250         }
39251         this.collapsed = false;
39252         if(this.config.animate && skipAnim !== true){
39253             this.animateExpand();
39254         }else{
39255             this.el.show();
39256             if(this.split){
39257                 this.split.el.show();
39258             }
39259             this.collapsedEl.setLocation(-2000,-2000);
39260             this.collapsedEl.hide();
39261             this.fireEvent("invalidated", this);
39262             this.fireEvent("expanded", this);
39263         }
39264     },
39265 */
39266     animateExpand : function(){
39267         // overridden
39268     },
39269
39270     initTabs : function()
39271     {
39272         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39273         
39274         var ts = new Roo.bootstrap.panel.Tabs({
39275             el: this.bodyEl.dom,
39276             region : this,
39277             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39278             disableTooltips: this.config.disableTabTips,
39279             toolbar : this.config.toolbar
39280         });
39281         
39282         if(this.config.hideTabs){
39283             ts.stripWrap.setDisplayed(false);
39284         }
39285         this.tabs = ts;
39286         ts.resizeTabs = this.config.resizeTabs === true;
39287         ts.minTabWidth = this.config.minTabWidth || 40;
39288         ts.maxTabWidth = this.config.maxTabWidth || 250;
39289         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39290         ts.monitorResize = false;
39291         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39292         ts.bodyEl.addClass('roo-layout-tabs-body');
39293         this.panels.each(this.initPanelAsTab, this);
39294     },
39295
39296     initPanelAsTab : function(panel){
39297         var ti = this.tabs.addTab(
39298             panel.getEl().id,
39299             panel.getTitle(),
39300             null,
39301             this.config.closeOnTab && panel.isClosable(),
39302             panel.tpl
39303         );
39304         if(panel.tabTip !== undefined){
39305             ti.setTooltip(panel.tabTip);
39306         }
39307         ti.on("activate", function(){
39308               this.setActivePanel(panel);
39309         }, this);
39310         
39311         if(this.config.closeOnTab){
39312             ti.on("beforeclose", function(t, e){
39313                 e.cancel = true;
39314                 this.remove(panel);
39315             }, this);
39316         }
39317         
39318         panel.tabItem = ti;
39319         
39320         return ti;
39321     },
39322
39323     updatePanelTitle : function(panel, title)
39324     {
39325         if(this.activePanel == panel){
39326             this.updateTitle(title);
39327         }
39328         if(this.tabs){
39329             var ti = this.tabs.getTab(panel.getEl().id);
39330             ti.setText(title);
39331             if(panel.tabTip !== undefined){
39332                 ti.setTooltip(panel.tabTip);
39333             }
39334         }
39335     },
39336
39337     updateTitle : function(title){
39338         if(this.titleTextEl && !this.config.title){
39339             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39340         }
39341     },
39342
39343     setActivePanel : function(panel)
39344     {
39345         panel = this.getPanel(panel);
39346         if(this.activePanel && this.activePanel != panel){
39347             if(this.activePanel.setActiveState(false) === false){
39348                 return;
39349             }
39350         }
39351         this.activePanel = panel;
39352         panel.setActiveState(true);
39353         if(this.panelSize){
39354             panel.setSize(this.panelSize.width, this.panelSize.height);
39355         }
39356         if(this.closeBtn){
39357             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39358         }
39359         this.updateTitle(panel.getTitle());
39360         if(this.tabs){
39361             this.fireEvent("invalidated", this);
39362         }
39363         this.fireEvent("panelactivated", this, panel);
39364     },
39365
39366     /**
39367      * Shows the specified panel.
39368      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39369      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39370      */
39371     showPanel : function(panel)
39372     {
39373         panel = this.getPanel(panel);
39374         if(panel){
39375             if(this.tabs){
39376                 var tab = this.tabs.getTab(panel.getEl().id);
39377                 if(tab.isHidden()){
39378                     this.tabs.unhideTab(tab.id);
39379                 }
39380                 tab.activate();
39381             }else{
39382                 this.setActivePanel(panel);
39383             }
39384         }
39385         return panel;
39386     },
39387
39388     /**
39389      * Get the active panel for this region.
39390      * @return {Roo.ContentPanel} The active panel or null
39391      */
39392     getActivePanel : function(){
39393         return this.activePanel;
39394     },
39395
39396     validateVisibility : function(){
39397         if(this.panels.getCount() < 1){
39398             this.updateTitle("&#160;");
39399             this.closeBtn.hide();
39400             this.hide();
39401         }else{
39402             if(!this.isVisible()){
39403                 this.show();
39404             }
39405         }
39406     },
39407
39408     /**
39409      * Adds the passed ContentPanel(s) to this region.
39410      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39411      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39412      */
39413     add : function(panel)
39414     {
39415         if(arguments.length > 1){
39416             for(var i = 0, len = arguments.length; i < len; i++) {
39417                 this.add(arguments[i]);
39418             }
39419             return null;
39420         }
39421         
39422         // if we have not been rendered yet, then we can not really do much of this..
39423         if (!this.bodyEl) {
39424             this.unrendered_panels.push(panel);
39425             return panel;
39426         }
39427         
39428         
39429         
39430         
39431         if(this.hasPanel(panel)){
39432             this.showPanel(panel);
39433             return panel;
39434         }
39435         panel.setRegion(this);
39436         this.panels.add(panel);
39437        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39438             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39439             // and hide them... ???
39440             this.bodyEl.dom.appendChild(panel.getEl().dom);
39441             if(panel.background !== true){
39442                 this.setActivePanel(panel);
39443             }
39444             this.fireEvent("paneladded", this, panel);
39445             return panel;
39446         }
39447         */
39448         if(!this.tabs){
39449             this.initTabs();
39450         }else{
39451             this.initPanelAsTab(panel);
39452         }
39453         
39454         
39455         if(panel.background !== true){
39456             this.tabs.activate(panel.getEl().id);
39457         }
39458         this.fireEvent("paneladded", this, panel);
39459         return panel;
39460     },
39461
39462     /**
39463      * Hides the tab for the specified panel.
39464      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39465      */
39466     hidePanel : function(panel){
39467         if(this.tabs && (panel = this.getPanel(panel))){
39468             this.tabs.hideTab(panel.getEl().id);
39469         }
39470     },
39471
39472     /**
39473      * Unhides the tab for a previously hidden panel.
39474      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39475      */
39476     unhidePanel : function(panel){
39477         if(this.tabs && (panel = this.getPanel(panel))){
39478             this.tabs.unhideTab(panel.getEl().id);
39479         }
39480     },
39481
39482     clearPanels : function(){
39483         while(this.panels.getCount() > 0){
39484              this.remove(this.panels.first());
39485         }
39486     },
39487
39488     /**
39489      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39490      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39491      * @param {Boolean} preservePanel Overrides the config preservePanel option
39492      * @return {Roo.ContentPanel} The panel that was removed
39493      */
39494     remove : function(panel, preservePanel)
39495     {
39496         panel = this.getPanel(panel);
39497         if(!panel){
39498             return null;
39499         }
39500         var e = {};
39501         this.fireEvent("beforeremove", this, panel, e);
39502         if(e.cancel === true){
39503             return null;
39504         }
39505         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39506         var panelId = panel.getId();
39507         this.panels.removeKey(panelId);
39508         if(preservePanel){
39509             document.body.appendChild(panel.getEl().dom);
39510         }
39511         if(this.tabs){
39512             this.tabs.removeTab(panel.getEl().id);
39513         }else if (!preservePanel){
39514             this.bodyEl.dom.removeChild(panel.getEl().dom);
39515         }
39516         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39517             var p = this.panels.first();
39518             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39519             tempEl.appendChild(p.getEl().dom);
39520             this.bodyEl.update("");
39521             this.bodyEl.dom.appendChild(p.getEl().dom);
39522             tempEl = null;
39523             this.updateTitle(p.getTitle());
39524             this.tabs = null;
39525             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39526             this.setActivePanel(p);
39527         }
39528         panel.setRegion(null);
39529         if(this.activePanel == panel){
39530             this.activePanel = null;
39531         }
39532         if(this.config.autoDestroy !== false && preservePanel !== true){
39533             try{panel.destroy();}catch(e){}
39534         }
39535         this.fireEvent("panelremoved", this, panel);
39536         return panel;
39537     },
39538
39539     /**
39540      * Returns the TabPanel component used by this region
39541      * @return {Roo.TabPanel}
39542      */
39543     getTabs : function(){
39544         return this.tabs;
39545     },
39546
39547     createTool : function(parentEl, className){
39548         var btn = Roo.DomHelper.append(parentEl, {
39549             tag: "div",
39550             cls: "x-layout-tools-button",
39551             children: [ {
39552                 tag: "div",
39553                 cls: "roo-layout-tools-button-inner " + className,
39554                 html: "&#160;"
39555             }]
39556         }, true);
39557         btn.addClassOnOver("roo-layout-tools-button-over");
39558         return btn;
39559     }
39560 });/*
39561  * Based on:
39562  * Ext JS Library 1.1.1
39563  * Copyright(c) 2006-2007, Ext JS, LLC.
39564  *
39565  * Originally Released Under LGPL - original licence link has changed is not relivant.
39566  *
39567  * Fork - LGPL
39568  * <script type="text/javascript">
39569  */
39570  
39571
39572
39573 /**
39574  * @class Roo.SplitLayoutRegion
39575  * @extends Roo.LayoutRegion
39576  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39577  */
39578 Roo.bootstrap.layout.Split = function(config){
39579     this.cursor = config.cursor;
39580     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39581 };
39582
39583 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39584 {
39585     splitTip : "Drag to resize.",
39586     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39587     useSplitTips : false,
39588
39589     applyConfig : function(config){
39590         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39591     },
39592     
39593     onRender : function(ctr,pos) {
39594         
39595         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39596         if(!this.config.split){
39597             return;
39598         }
39599         if(!this.split){
39600             
39601             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39602                             tag: "div",
39603                             id: this.el.id + "-split",
39604                             cls: "roo-layout-split roo-layout-split-"+this.position,
39605                             html: "&#160;"
39606             });
39607             /** The SplitBar for this region 
39608             * @type Roo.SplitBar */
39609             // does not exist yet...
39610             Roo.log([this.position, this.orientation]);
39611             
39612             this.split = new Roo.bootstrap.SplitBar({
39613                 dragElement : splitEl,
39614                 resizingElement: this.el,
39615                 orientation : this.orientation
39616             });
39617             
39618             this.split.on("moved", this.onSplitMove, this);
39619             this.split.useShim = this.config.useShim === true;
39620             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39621             if(this.useSplitTips){
39622                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39623             }
39624             //if(config.collapsible){
39625             //    this.split.el.on("dblclick", this.collapse,  this);
39626             //}
39627         }
39628         if(typeof this.config.minSize != "undefined"){
39629             this.split.minSize = this.config.minSize;
39630         }
39631         if(typeof this.config.maxSize != "undefined"){
39632             this.split.maxSize = this.config.maxSize;
39633         }
39634         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39635             this.hideSplitter();
39636         }
39637         
39638     },
39639
39640     getHMaxSize : function(){
39641          var cmax = this.config.maxSize || 10000;
39642          var center = this.mgr.getRegion("center");
39643          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39644     },
39645
39646     getVMaxSize : function(){
39647          var cmax = this.config.maxSize || 10000;
39648          var center = this.mgr.getRegion("center");
39649          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39650     },
39651
39652     onSplitMove : function(split, newSize){
39653         this.fireEvent("resized", this, newSize);
39654     },
39655     
39656     /** 
39657      * Returns the {@link Roo.SplitBar} for this region.
39658      * @return {Roo.SplitBar}
39659      */
39660     getSplitBar : function(){
39661         return this.split;
39662     },
39663     
39664     hide : function(){
39665         this.hideSplitter();
39666         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39667     },
39668
39669     hideSplitter : function(){
39670         if(this.split){
39671             this.split.el.setLocation(-2000,-2000);
39672             this.split.el.hide();
39673         }
39674     },
39675
39676     show : function(){
39677         if(this.split){
39678             this.split.el.show();
39679         }
39680         Roo.bootstrap.layout.Split.superclass.show.call(this);
39681     },
39682     
39683     beforeSlide: function(){
39684         if(Roo.isGecko){// firefox overflow auto bug workaround
39685             this.bodyEl.clip();
39686             if(this.tabs) {
39687                 this.tabs.bodyEl.clip();
39688             }
39689             if(this.activePanel){
39690                 this.activePanel.getEl().clip();
39691                 
39692                 if(this.activePanel.beforeSlide){
39693                     this.activePanel.beforeSlide();
39694                 }
39695             }
39696         }
39697     },
39698     
39699     afterSlide : function(){
39700         if(Roo.isGecko){// firefox overflow auto bug workaround
39701             this.bodyEl.unclip();
39702             if(this.tabs) {
39703                 this.tabs.bodyEl.unclip();
39704             }
39705             if(this.activePanel){
39706                 this.activePanel.getEl().unclip();
39707                 if(this.activePanel.afterSlide){
39708                     this.activePanel.afterSlide();
39709                 }
39710             }
39711         }
39712     },
39713
39714     initAutoHide : function(){
39715         if(this.autoHide !== false){
39716             if(!this.autoHideHd){
39717                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39718                 this.autoHideHd = {
39719                     "mouseout": function(e){
39720                         if(!e.within(this.el, true)){
39721                             st.delay(500);
39722                         }
39723                     },
39724                     "mouseover" : function(e){
39725                         st.cancel();
39726                     },
39727                     scope : this
39728                 };
39729             }
39730             this.el.on(this.autoHideHd);
39731         }
39732     },
39733
39734     clearAutoHide : function(){
39735         if(this.autoHide !== false){
39736             this.el.un("mouseout", this.autoHideHd.mouseout);
39737             this.el.un("mouseover", this.autoHideHd.mouseover);
39738         }
39739     },
39740
39741     clearMonitor : function(){
39742         Roo.get(document).un("click", this.slideInIf, this);
39743     },
39744
39745     // these names are backwards but not changed for compat
39746     slideOut : function(){
39747         if(this.isSlid || this.el.hasActiveFx()){
39748             return;
39749         }
39750         this.isSlid = true;
39751         if(this.collapseBtn){
39752             this.collapseBtn.hide();
39753         }
39754         this.closeBtnState = this.closeBtn.getStyle('display');
39755         this.closeBtn.hide();
39756         if(this.stickBtn){
39757             this.stickBtn.show();
39758         }
39759         this.el.show();
39760         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39761         this.beforeSlide();
39762         this.el.setStyle("z-index", 10001);
39763         this.el.slideIn(this.getSlideAnchor(), {
39764             callback: function(){
39765                 this.afterSlide();
39766                 this.initAutoHide();
39767                 Roo.get(document).on("click", this.slideInIf, this);
39768                 this.fireEvent("slideshow", this);
39769             },
39770             scope: this,
39771             block: true
39772         });
39773     },
39774
39775     afterSlideIn : function(){
39776         this.clearAutoHide();
39777         this.isSlid = false;
39778         this.clearMonitor();
39779         this.el.setStyle("z-index", "");
39780         if(this.collapseBtn){
39781             this.collapseBtn.show();
39782         }
39783         this.closeBtn.setStyle('display', this.closeBtnState);
39784         if(this.stickBtn){
39785             this.stickBtn.hide();
39786         }
39787         this.fireEvent("slidehide", this);
39788     },
39789
39790     slideIn : function(cb){
39791         if(!this.isSlid || this.el.hasActiveFx()){
39792             Roo.callback(cb);
39793             return;
39794         }
39795         this.isSlid = false;
39796         this.beforeSlide();
39797         this.el.slideOut(this.getSlideAnchor(), {
39798             callback: function(){
39799                 this.el.setLeftTop(-10000, -10000);
39800                 this.afterSlide();
39801                 this.afterSlideIn();
39802                 Roo.callback(cb);
39803             },
39804             scope: this,
39805             block: true
39806         });
39807     },
39808     
39809     slideInIf : function(e){
39810         if(!e.within(this.el)){
39811             this.slideIn();
39812         }
39813     },
39814
39815     animateCollapse : function(){
39816         this.beforeSlide();
39817         this.el.setStyle("z-index", 20000);
39818         var anchor = this.getSlideAnchor();
39819         this.el.slideOut(anchor, {
39820             callback : function(){
39821                 this.el.setStyle("z-index", "");
39822                 this.collapsedEl.slideIn(anchor, {duration:.3});
39823                 this.afterSlide();
39824                 this.el.setLocation(-10000,-10000);
39825                 this.el.hide();
39826                 this.fireEvent("collapsed", this);
39827             },
39828             scope: this,
39829             block: true
39830         });
39831     },
39832
39833     animateExpand : function(){
39834         this.beforeSlide();
39835         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39836         this.el.setStyle("z-index", 20000);
39837         this.collapsedEl.hide({
39838             duration:.1
39839         });
39840         this.el.slideIn(this.getSlideAnchor(), {
39841             callback : function(){
39842                 this.el.setStyle("z-index", "");
39843                 this.afterSlide();
39844                 if(this.split){
39845                     this.split.el.show();
39846                 }
39847                 this.fireEvent("invalidated", this);
39848                 this.fireEvent("expanded", this);
39849             },
39850             scope: this,
39851             block: true
39852         });
39853     },
39854
39855     anchors : {
39856         "west" : "left",
39857         "east" : "right",
39858         "north" : "top",
39859         "south" : "bottom"
39860     },
39861
39862     sanchors : {
39863         "west" : "l",
39864         "east" : "r",
39865         "north" : "t",
39866         "south" : "b"
39867     },
39868
39869     canchors : {
39870         "west" : "tl-tr",
39871         "east" : "tr-tl",
39872         "north" : "tl-bl",
39873         "south" : "bl-tl"
39874     },
39875
39876     getAnchor : function(){
39877         return this.anchors[this.position];
39878     },
39879
39880     getCollapseAnchor : function(){
39881         return this.canchors[this.position];
39882     },
39883
39884     getSlideAnchor : function(){
39885         return this.sanchors[this.position];
39886     },
39887
39888     getAlignAdj : function(){
39889         var cm = this.cmargins;
39890         switch(this.position){
39891             case "west":
39892                 return [0, 0];
39893             break;
39894             case "east":
39895                 return [0, 0];
39896             break;
39897             case "north":
39898                 return [0, 0];
39899             break;
39900             case "south":
39901                 return [0, 0];
39902             break;
39903         }
39904     },
39905
39906     getExpandAdj : function(){
39907         var c = this.collapsedEl, cm = this.cmargins;
39908         switch(this.position){
39909             case "west":
39910                 return [-(cm.right+c.getWidth()+cm.left), 0];
39911             break;
39912             case "east":
39913                 return [cm.right+c.getWidth()+cm.left, 0];
39914             break;
39915             case "north":
39916                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39917             break;
39918             case "south":
39919                 return [0, cm.top+cm.bottom+c.getHeight()];
39920             break;
39921         }
39922     }
39923 });/*
39924  * Based on:
39925  * Ext JS Library 1.1.1
39926  * Copyright(c) 2006-2007, Ext JS, LLC.
39927  *
39928  * Originally Released Under LGPL - original licence link has changed is not relivant.
39929  *
39930  * Fork - LGPL
39931  * <script type="text/javascript">
39932  */
39933 /*
39934  * These classes are private internal classes
39935  */
39936 Roo.bootstrap.layout.Center = function(config){
39937     config.region = "center";
39938     Roo.bootstrap.layout.Region.call(this, config);
39939     this.visible = true;
39940     this.minWidth = config.minWidth || 20;
39941     this.minHeight = config.minHeight || 20;
39942 };
39943
39944 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39945     hide : function(){
39946         // center panel can't be hidden
39947     },
39948     
39949     show : function(){
39950         // center panel can't be hidden
39951     },
39952     
39953     getMinWidth: function(){
39954         return this.minWidth;
39955     },
39956     
39957     getMinHeight: function(){
39958         return this.minHeight;
39959     }
39960 });
39961
39962
39963
39964
39965  
39966
39967
39968
39969
39970
39971
39972 Roo.bootstrap.layout.North = function(config)
39973 {
39974     config.region = 'north';
39975     config.cursor = 'n-resize';
39976     
39977     Roo.bootstrap.layout.Split.call(this, config);
39978     
39979     
39980     if(this.split){
39981         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39982         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39983         this.split.el.addClass("roo-layout-split-v");
39984     }
39985     //var size = config.initialSize || config.height;
39986     //if(this.el && typeof size != "undefined"){
39987     //    this.el.setHeight(size);
39988     //}
39989 };
39990 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39991 {
39992     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39993      
39994      
39995     onRender : function(ctr, pos)
39996     {
39997         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39998         var size = this.config.initialSize || this.config.height;
39999         if(this.el && typeof size != "undefined"){
40000             this.el.setHeight(size);
40001         }
40002     
40003     },
40004     
40005     getBox : function(){
40006         if(this.collapsed){
40007             return this.collapsedEl.getBox();
40008         }
40009         var box = this.el.getBox();
40010         if(this.split){
40011             box.height += this.split.el.getHeight();
40012         }
40013         return box;
40014     },
40015     
40016     updateBox : function(box){
40017         if(this.split && !this.collapsed){
40018             box.height -= this.split.el.getHeight();
40019             this.split.el.setLeft(box.x);
40020             this.split.el.setTop(box.y+box.height);
40021             this.split.el.setWidth(box.width);
40022         }
40023         if(this.collapsed){
40024             this.updateBody(box.width, null);
40025         }
40026         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40027     }
40028 });
40029
40030
40031
40032
40033
40034 Roo.bootstrap.layout.South = function(config){
40035     config.region = 'south';
40036     config.cursor = 's-resize';
40037     Roo.bootstrap.layout.Split.call(this, config);
40038     if(this.split){
40039         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40040         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40041         this.split.el.addClass("roo-layout-split-v");
40042     }
40043     
40044 };
40045
40046 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40047     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40048     
40049     onRender : function(ctr, pos)
40050     {
40051         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40052         var size = this.config.initialSize || this.config.height;
40053         if(this.el && typeof size != "undefined"){
40054             this.el.setHeight(size);
40055         }
40056     
40057     },
40058     
40059     getBox : function(){
40060         if(this.collapsed){
40061             return this.collapsedEl.getBox();
40062         }
40063         var box = this.el.getBox();
40064         if(this.split){
40065             var sh = this.split.el.getHeight();
40066             box.height += sh;
40067             box.y -= sh;
40068         }
40069         return box;
40070     },
40071     
40072     updateBox : function(box){
40073         if(this.split && !this.collapsed){
40074             var sh = this.split.el.getHeight();
40075             box.height -= sh;
40076             box.y += sh;
40077             this.split.el.setLeft(box.x);
40078             this.split.el.setTop(box.y-sh);
40079             this.split.el.setWidth(box.width);
40080         }
40081         if(this.collapsed){
40082             this.updateBody(box.width, null);
40083         }
40084         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40085     }
40086 });
40087
40088 Roo.bootstrap.layout.East = function(config){
40089     config.region = "east";
40090     config.cursor = "e-resize";
40091     Roo.bootstrap.layout.Split.call(this, config);
40092     if(this.split){
40093         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40094         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40095         this.split.el.addClass("roo-layout-split-h");
40096     }
40097     
40098 };
40099 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40100     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40101     
40102     onRender : function(ctr, pos)
40103     {
40104         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40105         var size = this.config.initialSize || this.config.width;
40106         if(this.el && typeof size != "undefined"){
40107             this.el.setWidth(size);
40108         }
40109     
40110     },
40111     
40112     getBox : function(){
40113         if(this.collapsed){
40114             return this.collapsedEl.getBox();
40115         }
40116         var box = this.el.getBox();
40117         if(this.split){
40118             var sw = this.split.el.getWidth();
40119             box.width += sw;
40120             box.x -= sw;
40121         }
40122         return box;
40123     },
40124
40125     updateBox : function(box){
40126         if(this.split && !this.collapsed){
40127             var sw = this.split.el.getWidth();
40128             box.width -= sw;
40129             this.split.el.setLeft(box.x);
40130             this.split.el.setTop(box.y);
40131             this.split.el.setHeight(box.height);
40132             box.x += sw;
40133         }
40134         if(this.collapsed){
40135             this.updateBody(null, box.height);
40136         }
40137         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40138     }
40139 });
40140
40141 Roo.bootstrap.layout.West = function(config){
40142     config.region = "west";
40143     config.cursor = "w-resize";
40144     
40145     Roo.bootstrap.layout.Split.call(this, config);
40146     if(this.split){
40147         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40148         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40149         this.split.el.addClass("roo-layout-split-h");
40150     }
40151     
40152 };
40153 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40154     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40155     
40156     onRender: function(ctr, pos)
40157     {
40158         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40159         var size = this.config.initialSize || this.config.width;
40160         if(typeof size != "undefined"){
40161             this.el.setWidth(size);
40162         }
40163     },
40164     
40165     getBox : function(){
40166         if(this.collapsed){
40167             return this.collapsedEl.getBox();
40168         }
40169         var box = this.el.getBox();
40170         if (box.width == 0) {
40171             box.width = this.config.width; // kludge?
40172         }
40173         if(this.split){
40174             box.width += this.split.el.getWidth();
40175         }
40176         return box;
40177     },
40178     
40179     updateBox : function(box){
40180         if(this.split && !this.collapsed){
40181             var sw = this.split.el.getWidth();
40182             box.width -= sw;
40183             this.split.el.setLeft(box.x+box.width);
40184             this.split.el.setTop(box.y);
40185             this.split.el.setHeight(box.height);
40186         }
40187         if(this.collapsed){
40188             this.updateBody(null, box.height);
40189         }
40190         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40191     }
40192 });/*
40193  * Based on:
40194  * Ext JS Library 1.1.1
40195  * Copyright(c) 2006-2007, Ext JS, LLC.
40196  *
40197  * Originally Released Under LGPL - original licence link has changed is not relivant.
40198  *
40199  * Fork - LGPL
40200  * <script type="text/javascript">
40201  */
40202 /**
40203  * @class Roo.bootstrap.paenl.Content
40204  * @extends Roo.util.Observable
40205  * @builder-top
40206  * @children Roo.bootstrap.Component
40207  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40208  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40209  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40210  * @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
40211  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40212  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40213  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40214  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40215  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40216  * @cfg {String} title          The title for this panel
40217  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40218  * @cfg {String} url            Calls {@link #setUrl} with this value
40219  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40220  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40221  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40222  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40223  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40224  * @cfg {Boolean} badges render the badges
40225  * @cfg {String} cls  extra classes to use  
40226  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40227  
40228  * @constructor
40229  * Create a new ContentPanel.
40230  * @param {String/Object} config A string to set only the title or a config object
40231  
40232  */
40233 Roo.bootstrap.panel.Content = function( config){
40234     
40235     this.tpl = config.tpl || false;
40236     
40237     var el = config.el;
40238     var content = config.content;
40239
40240     if(config.autoCreate){ // xtype is available if this is called from factory
40241         el = Roo.id();
40242     }
40243     this.el = Roo.get(el);
40244     if(!this.el && config && config.autoCreate){
40245         if(typeof config.autoCreate == "object"){
40246             if(!config.autoCreate.id){
40247                 config.autoCreate.id = config.id||el;
40248             }
40249             this.el = Roo.DomHelper.append(document.body,
40250                         config.autoCreate, true);
40251         }else{
40252             var elcfg =  {
40253                 tag: "div",
40254                 cls: (config.cls || '') +
40255                     (config.background ? ' bg-' + config.background : '') +
40256                     " roo-layout-inactive-content",
40257                 id: config.id||el
40258             };
40259             if (config.iframe) {
40260                 elcfg.cn = [
40261                     {
40262                         tag : 'iframe',
40263                         style : 'border: 0px',
40264                         src : 'about:blank'
40265                     }
40266                 ];
40267             }
40268               
40269             if (config.html) {
40270                 elcfg.html = config.html;
40271                 
40272             }
40273                         
40274             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40275             if (config.iframe) {
40276                 this.iframeEl = this.el.select('iframe',true).first();
40277             }
40278             
40279         }
40280     } 
40281     this.closable = false;
40282     this.loaded = false;
40283     this.active = false;
40284    
40285       
40286     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40287         
40288         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40289         
40290         this.wrapEl = this.el; //this.el.wrap();
40291         var ti = [];
40292         if (config.toolbar.items) {
40293             ti = config.toolbar.items ;
40294             delete config.toolbar.items ;
40295         }
40296         
40297         var nitems = [];
40298         this.toolbar.render(this.wrapEl, 'before');
40299         for(var i =0;i < ti.length;i++) {
40300           //  Roo.log(['add child', items[i]]);
40301             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40302         }
40303         this.toolbar.items = nitems;
40304         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40305         delete config.toolbar;
40306         
40307     }
40308     /*
40309     // xtype created footer. - not sure if will work as we normally have to render first..
40310     if (this.footer && !this.footer.el && this.footer.xtype) {
40311         if (!this.wrapEl) {
40312             this.wrapEl = this.el.wrap();
40313         }
40314     
40315         this.footer.container = this.wrapEl.createChild();
40316          
40317         this.footer = Roo.factory(this.footer, Roo);
40318         
40319     }
40320     */
40321     
40322      if(typeof config == "string"){
40323         this.title = config;
40324     }else{
40325         Roo.apply(this, config);
40326     }
40327     
40328     if(this.resizeEl){
40329         this.resizeEl = Roo.get(this.resizeEl, true);
40330     }else{
40331         this.resizeEl = this.el;
40332     }
40333     // handle view.xtype
40334     
40335  
40336     
40337     
40338     this.addEvents({
40339         /**
40340          * @event activate
40341          * Fires when this panel is activated. 
40342          * @param {Roo.ContentPanel} this
40343          */
40344         "activate" : true,
40345         /**
40346          * @event deactivate
40347          * Fires when this panel is activated. 
40348          * @param {Roo.ContentPanel} this
40349          */
40350         "deactivate" : true,
40351
40352         /**
40353          * @event resize
40354          * Fires when this panel is resized if fitToFrame is true.
40355          * @param {Roo.ContentPanel} this
40356          * @param {Number} width The width after any component adjustments
40357          * @param {Number} height The height after any component adjustments
40358          */
40359         "resize" : true,
40360         
40361          /**
40362          * @event render
40363          * Fires when this tab is created
40364          * @param {Roo.ContentPanel} this
40365          */
40366         "render" : true,
40367         
40368           /**
40369          * @event scroll
40370          * Fires when this content is scrolled
40371          * @param {Roo.ContentPanel} this
40372          * @param {Event} scrollEvent
40373          */
40374         "scroll" : true
40375         
40376         
40377         
40378     });
40379     
40380
40381     
40382     
40383     if(this.autoScroll && !this.iframe){
40384         this.resizeEl.setStyle("overflow", "auto");
40385         this.resizeEl.on('scroll', this.onScroll, this);
40386     } else {
40387         // fix randome scrolling
40388         //this.el.on('scroll', function() {
40389         //    Roo.log('fix random scolling');
40390         //    this.scrollTo('top',0); 
40391         //});
40392     }
40393     content = content || this.content;
40394     if(content){
40395         this.setContent(content);
40396     }
40397     if(config && config.url){
40398         this.setUrl(this.url, this.params, this.loadOnce);
40399     }
40400     
40401     
40402     
40403     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40404     
40405     if (this.view && typeof(this.view.xtype) != 'undefined') {
40406         this.view.el = this.el.appendChild(document.createElement("div"));
40407         this.view = Roo.factory(this.view); 
40408         this.view.render  &&  this.view.render(false, '');  
40409     }
40410     
40411     
40412     this.fireEvent('render', this);
40413 };
40414
40415 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40416     
40417     cls : '',
40418     background : '',
40419     
40420     tabTip : '',
40421     
40422     iframe : false,
40423     iframeEl : false,
40424     
40425     /* Resize Element - use this to work out scroll etc. */
40426     resizeEl : false,
40427     
40428     setRegion : function(region){
40429         this.region = region;
40430         this.setActiveClass(region && !this.background);
40431     },
40432     
40433     
40434     setActiveClass: function(state)
40435     {
40436         if(state){
40437            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40438            this.el.setStyle('position','relative');
40439         }else{
40440            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40441            this.el.setStyle('position', 'absolute');
40442         } 
40443     },
40444     
40445     /**
40446      * Returns the toolbar for this Panel if one was configured. 
40447      * @return {Roo.Toolbar} 
40448      */
40449     getToolbar : function(){
40450         return this.toolbar;
40451     },
40452     
40453     setActiveState : function(active)
40454     {
40455         this.active = active;
40456         this.setActiveClass(active);
40457         if(!active){
40458             if(this.fireEvent("deactivate", this) === false){
40459                 return false;
40460             }
40461             return true;
40462         }
40463         this.fireEvent("activate", this);
40464         return true;
40465     },
40466     /**
40467      * Updates this panel's element (not for iframe)
40468      * @param {String} content The new content
40469      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40470     */
40471     setContent : function(content, loadScripts){
40472         if (this.iframe) {
40473             return;
40474         }
40475         
40476         this.el.update(content, loadScripts);
40477     },
40478
40479     ignoreResize : function(w, h){
40480         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40481             return true;
40482         }else{
40483             this.lastSize = {width: w, height: h};
40484             return false;
40485         }
40486     },
40487     /**
40488      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40489      * @return {Roo.UpdateManager} The UpdateManager
40490      */
40491     getUpdateManager : function(){
40492         if (this.iframe) {
40493             return false;
40494         }
40495         return this.el.getUpdateManager();
40496     },
40497      /**
40498      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40499      * Does not work with IFRAME contents
40500      * @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:
40501 <pre><code>
40502 panel.load({
40503     url: "your-url.php",
40504     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40505     callback: yourFunction,
40506     scope: yourObject, //(optional scope)
40507     discardUrl: false,
40508     nocache: false,
40509     text: "Loading...",
40510     timeout: 30,
40511     scripts: false
40512 });
40513 </code></pre>
40514      
40515      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40516      * 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.
40517      * @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}
40518      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40519      * @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.
40520      * @return {Roo.ContentPanel} this
40521      */
40522     load : function(){
40523         
40524         if (this.iframe) {
40525             return this;
40526         }
40527         
40528         var um = this.el.getUpdateManager();
40529         um.update.apply(um, arguments);
40530         return this;
40531     },
40532
40533
40534     /**
40535      * 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.
40536      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40537      * @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)
40538      * @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)
40539      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40540      */
40541     setUrl : function(url, params, loadOnce){
40542         if (this.iframe) {
40543             this.iframeEl.dom.src = url;
40544             return false;
40545         }
40546         
40547         if(this.refreshDelegate){
40548             this.removeListener("activate", this.refreshDelegate);
40549         }
40550         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40551         this.on("activate", this.refreshDelegate);
40552         return this.el.getUpdateManager();
40553     },
40554     
40555     _handleRefresh : function(url, params, loadOnce){
40556         if(!loadOnce || !this.loaded){
40557             var updater = this.el.getUpdateManager();
40558             updater.update(url, params, this._setLoaded.createDelegate(this));
40559         }
40560     },
40561     
40562     _setLoaded : function(){
40563         this.loaded = true;
40564     }, 
40565     
40566     /**
40567      * Returns this panel's id
40568      * @return {String} 
40569      */
40570     getId : function(){
40571         return this.el.id;
40572     },
40573     
40574     /** 
40575      * Returns this panel's element - used by regiosn to add.
40576      * @return {Roo.Element} 
40577      */
40578     getEl : function(){
40579         return this.wrapEl || this.el;
40580     },
40581     
40582    
40583     
40584     adjustForComponents : function(width, height)
40585     {
40586         //Roo.log('adjustForComponents ');
40587         if(this.resizeEl != this.el){
40588             width -= this.el.getFrameWidth('lr');
40589             height -= this.el.getFrameWidth('tb');
40590         }
40591         if(this.toolbar){
40592             var te = this.toolbar.getEl();
40593             te.setWidth(width);
40594             height -= te.getHeight();
40595         }
40596         if(this.footer){
40597             var te = this.footer.getEl();
40598             te.setWidth(width);
40599             height -= te.getHeight();
40600         }
40601         
40602         
40603         if(this.adjustments){
40604             width += this.adjustments[0];
40605             height += this.adjustments[1];
40606         }
40607         return {"width": width, "height": height};
40608     },
40609     
40610     setSize : function(width, height){
40611         if(this.fitToFrame && !this.ignoreResize(width, height)){
40612             if(this.fitContainer && this.resizeEl != this.el){
40613                 this.el.setSize(width, height);
40614             }
40615             var size = this.adjustForComponents(width, height);
40616             if (this.iframe) {
40617                 this.iframeEl.setSize(width,height);
40618             }
40619             
40620             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40621             this.fireEvent('resize', this, size.width, size.height);
40622             
40623             
40624         }
40625     },
40626     
40627     /**
40628      * Returns this panel's title
40629      * @return {String} 
40630      */
40631     getTitle : function(){
40632         
40633         if (typeof(this.title) != 'object') {
40634             return this.title;
40635         }
40636         
40637         var t = '';
40638         for (var k in this.title) {
40639             if (!this.title.hasOwnProperty(k)) {
40640                 continue;
40641             }
40642             
40643             if (k.indexOf('-') >= 0) {
40644                 var s = k.split('-');
40645                 for (var i = 0; i<s.length; i++) {
40646                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40647                 }
40648             } else {
40649                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40650             }
40651         }
40652         return t;
40653     },
40654     
40655     /**
40656      * Set this panel's title
40657      * @param {String} title
40658      */
40659     setTitle : function(title){
40660         this.title = title;
40661         if(this.region){
40662             this.region.updatePanelTitle(this, title);
40663         }
40664     },
40665     
40666     /**
40667      * Returns true is this panel was configured to be closable
40668      * @return {Boolean} 
40669      */
40670     isClosable : function(){
40671         return this.closable;
40672     },
40673     
40674     beforeSlide : function(){
40675         this.el.clip();
40676         this.resizeEl.clip();
40677     },
40678     
40679     afterSlide : function(){
40680         this.el.unclip();
40681         this.resizeEl.unclip();
40682     },
40683     
40684     /**
40685      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40686      *   Will fail silently if the {@link #setUrl} method has not been called.
40687      *   This does not activate the panel, just updates its content.
40688      */
40689     refresh : function(){
40690         if(this.refreshDelegate){
40691            this.loaded = false;
40692            this.refreshDelegate();
40693         }
40694     },
40695     
40696     /**
40697      * Destroys this panel
40698      */
40699     destroy : function(){
40700         this.el.removeAllListeners();
40701         var tempEl = document.createElement("span");
40702         tempEl.appendChild(this.el.dom);
40703         tempEl.innerHTML = "";
40704         this.el.remove();
40705         this.el = null;
40706     },
40707     
40708     /**
40709      * form - if the content panel contains a form - this is a reference to it.
40710      * @type {Roo.form.Form}
40711      */
40712     form : false,
40713     /**
40714      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40715      *    This contains a reference to it.
40716      * @type {Roo.View}
40717      */
40718     view : false,
40719     
40720       /**
40721      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40722      * <pre><code>
40723
40724 layout.addxtype({
40725        xtype : 'Form',
40726        items: [ .... ]
40727    }
40728 );
40729
40730 </code></pre>
40731      * @param {Object} cfg Xtype definition of item to add.
40732      */
40733     
40734     
40735     getChildContainer: function () {
40736         return this.getEl();
40737     },
40738     
40739     
40740     onScroll : function(e)
40741     {
40742         this.fireEvent('scroll', this, e);
40743     }
40744     
40745     
40746     /*
40747         var  ret = new Roo.factory(cfg);
40748         return ret;
40749         
40750         
40751         // add form..
40752         if (cfg.xtype.match(/^Form$/)) {
40753             
40754             var el;
40755             //if (this.footer) {
40756             //    el = this.footer.container.insertSibling(false, 'before');
40757             //} else {
40758                 el = this.el.createChild();
40759             //}
40760
40761             this.form = new  Roo.form.Form(cfg);
40762             
40763             
40764             if ( this.form.allItems.length) {
40765                 this.form.render(el.dom);
40766             }
40767             return this.form;
40768         }
40769         // should only have one of theses..
40770         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40771             // views.. should not be just added - used named prop 'view''
40772             
40773             cfg.el = this.el.appendChild(document.createElement("div"));
40774             // factory?
40775             
40776             var ret = new Roo.factory(cfg);
40777              
40778              ret.render && ret.render(false, ''); // render blank..
40779             this.view = ret;
40780             return ret;
40781         }
40782         return false;
40783     }
40784     \*/
40785 });
40786  
40787 /**
40788  * @class Roo.bootstrap.panel.Grid
40789  * @extends Roo.bootstrap.panel.Content
40790  * @constructor
40791  * Create a new GridPanel.
40792  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40793  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40794  * @param {Object} config A the config object
40795   
40796  */
40797
40798
40799
40800 Roo.bootstrap.panel.Grid = function(config)
40801 {
40802     
40803       
40804     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40805         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40806
40807     config.el = this.wrapper;
40808     //this.el = this.wrapper;
40809     
40810       if (config.container) {
40811         // ctor'ed from a Border/panel.grid
40812         
40813         
40814         this.wrapper.setStyle("overflow", "hidden");
40815         this.wrapper.addClass('roo-grid-container');
40816
40817     }
40818     
40819     
40820     if(config.toolbar){
40821         var tool_el = this.wrapper.createChild();    
40822         this.toolbar = Roo.factory(config.toolbar);
40823         var ti = [];
40824         if (config.toolbar.items) {
40825             ti = config.toolbar.items ;
40826             delete config.toolbar.items ;
40827         }
40828         
40829         var nitems = [];
40830         this.toolbar.render(tool_el);
40831         for(var i =0;i < ti.length;i++) {
40832           //  Roo.log(['add child', items[i]]);
40833             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40834         }
40835         this.toolbar.items = nitems;
40836         
40837         delete config.toolbar;
40838     }
40839     
40840     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40841     config.grid.scrollBody = true;;
40842     config.grid.monitorWindowResize = false; // turn off autosizing
40843     config.grid.autoHeight = false;
40844     config.grid.autoWidth = false;
40845     
40846     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40847     
40848     if (config.background) {
40849         // render grid on panel activation (if panel background)
40850         this.on('activate', function(gp) {
40851             if (!gp.grid.rendered) {
40852                 gp.grid.render(this.wrapper);
40853                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40854             }
40855         });
40856             
40857     } else {
40858         this.grid.render(this.wrapper);
40859         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40860
40861     }
40862     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40863     // ??? needed ??? config.el = this.wrapper;
40864     
40865     
40866     
40867   
40868     // xtype created footer. - not sure if will work as we normally have to render first..
40869     if (this.footer && !this.footer.el && this.footer.xtype) {
40870         
40871         var ctr = this.grid.getView().getFooterPanel(true);
40872         this.footer.dataSource = this.grid.dataSource;
40873         this.footer = Roo.factory(this.footer, Roo);
40874         this.footer.render(ctr);
40875         
40876     }
40877     
40878     
40879     
40880     
40881      
40882 };
40883
40884 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40885     getId : function(){
40886         return this.grid.id;
40887     },
40888     
40889     /**
40890      * Returns the grid for this panel
40891      * @return {Roo.bootstrap.Table} 
40892      */
40893     getGrid : function(){
40894         return this.grid;    
40895     },
40896     
40897     setSize : function(width, height){
40898         if(!this.ignoreResize(width, height)){
40899             var grid = this.grid;
40900             var size = this.adjustForComponents(width, height);
40901             // tfoot is not a footer?
40902           
40903             
40904             var gridel = grid.getGridEl();
40905             gridel.setSize(size.width, size.height);
40906             
40907             var tbd = grid.getGridEl().select('tbody', true).first();
40908             var thd = grid.getGridEl().select('thead',true).first();
40909             var tbf= grid.getGridEl().select('tfoot', true).first();
40910
40911             if (tbf) {
40912                 size.height -= tbf.getHeight();
40913             }
40914             if (thd) {
40915                 size.height -= thd.getHeight();
40916             }
40917             
40918             tbd.setSize(size.width, size.height );
40919             // this is for the account management tab -seems to work there.
40920             var thd = grid.getGridEl().select('thead',true).first();
40921             //if (tbd) {
40922             //    tbd.setSize(size.width, size.height - thd.getHeight());
40923             //}
40924              
40925             grid.autoSize();
40926         }
40927     },
40928      
40929     
40930     
40931     beforeSlide : function(){
40932         this.grid.getView().scroller.clip();
40933     },
40934     
40935     afterSlide : function(){
40936         this.grid.getView().scroller.unclip();
40937     },
40938     
40939     destroy : function(){
40940         this.grid.destroy();
40941         delete this.grid;
40942         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40943     }
40944 });
40945
40946 /**
40947  * @class Roo.bootstrap.panel.Nest
40948  * @extends Roo.bootstrap.panel.Content
40949  * @constructor
40950  * Create a new Panel, that can contain a layout.Border.
40951  * 
40952  * 
40953  * @param {String/Object} config A string to set only the title or a config object
40954  */
40955 Roo.bootstrap.panel.Nest = function(config)
40956 {
40957     // construct with only one argument..
40958     /* FIXME - implement nicer consturctors
40959     if (layout.layout) {
40960         config = layout;
40961         layout = config.layout;
40962         delete config.layout;
40963     }
40964     if (layout.xtype && !layout.getEl) {
40965         // then layout needs constructing..
40966         layout = Roo.factory(layout, Roo);
40967     }
40968     */
40969     
40970     config.el =  config.layout.getEl();
40971     
40972     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40973     
40974     config.layout.monitorWindowResize = false; // turn off autosizing
40975     this.layout = config.layout;
40976     this.layout.getEl().addClass("roo-layout-nested-layout");
40977     this.layout.parent = this;
40978     
40979     
40980     
40981     
40982 };
40983
40984 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40985     /**
40986     * @cfg {Roo.BorderLayout} layout The layout for this panel
40987     */
40988     layout : false,
40989
40990     setSize : function(width, height){
40991         if(!this.ignoreResize(width, height)){
40992             var size = this.adjustForComponents(width, height);
40993             var el = this.layout.getEl();
40994             if (size.height < 1) {
40995                 el.setWidth(size.width);   
40996             } else {
40997                 el.setSize(size.width, size.height);
40998             }
40999             var touch = el.dom.offsetWidth;
41000             this.layout.layout();
41001             // ie requires a double layout on the first pass
41002             if(Roo.isIE && !this.initialized){
41003                 this.initialized = true;
41004                 this.layout.layout();
41005             }
41006         }
41007     },
41008     
41009     // activate all subpanels if not currently active..
41010     
41011     setActiveState : function(active){
41012         this.active = active;
41013         this.setActiveClass(active);
41014         
41015         if(!active){
41016             this.fireEvent("deactivate", this);
41017             return;
41018         }
41019         
41020         this.fireEvent("activate", this);
41021         // not sure if this should happen before or after..
41022         if (!this.layout) {
41023             return; // should not happen..
41024         }
41025         var reg = false;
41026         for (var r in this.layout.regions) {
41027             reg = this.layout.getRegion(r);
41028             if (reg.getActivePanel()) {
41029                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41030                 reg.setActivePanel(reg.getActivePanel());
41031                 continue;
41032             }
41033             if (!reg.panels.length) {
41034                 continue;
41035             }
41036             reg.showPanel(reg.getPanel(0));
41037         }
41038         
41039         
41040         
41041         
41042     },
41043     
41044     /**
41045      * Returns the nested BorderLayout for this panel
41046      * @return {Roo.BorderLayout} 
41047      */
41048     getLayout : function(){
41049         return this.layout;
41050     },
41051     
41052      /**
41053      * Adds a xtype elements to the layout of the nested panel
41054      * <pre><code>
41055
41056 panel.addxtype({
41057        xtype : 'ContentPanel',
41058        region: 'west',
41059        items: [ .... ]
41060    }
41061 );
41062
41063 panel.addxtype({
41064         xtype : 'NestedLayoutPanel',
41065         region: 'west',
41066         layout: {
41067            center: { },
41068            west: { }   
41069         },
41070         items : [ ... list of content panels or nested layout panels.. ]
41071    }
41072 );
41073 </code></pre>
41074      * @param {Object} cfg Xtype definition of item to add.
41075      */
41076     addxtype : function(cfg) {
41077         return this.layout.addxtype(cfg);
41078     
41079     }
41080 });/*
41081  * Based on:
41082  * Ext JS Library 1.1.1
41083  * Copyright(c) 2006-2007, Ext JS, LLC.
41084  *
41085  * Originally Released Under LGPL - original licence link has changed is not relivant.
41086  *
41087  * Fork - LGPL
41088  * <script type="text/javascript">
41089  */
41090 /**
41091  * @class Roo.TabPanel
41092  * @extends Roo.util.Observable
41093  * A lightweight tab container.
41094  * <br><br>
41095  * Usage:
41096  * <pre><code>
41097 // basic tabs 1, built from existing content
41098 var tabs = new Roo.TabPanel("tabs1");
41099 tabs.addTab("script", "View Script");
41100 tabs.addTab("markup", "View Markup");
41101 tabs.activate("script");
41102
41103 // more advanced tabs, built from javascript
41104 var jtabs = new Roo.TabPanel("jtabs");
41105 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41106
41107 // set up the UpdateManager
41108 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41109 var updater = tab2.getUpdateManager();
41110 updater.setDefaultUrl("ajax1.htm");
41111 tab2.on('activate', updater.refresh, updater, true);
41112
41113 // Use setUrl for Ajax loading
41114 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41115 tab3.setUrl("ajax2.htm", null, true);
41116
41117 // Disabled tab
41118 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41119 tab4.disable();
41120
41121 jtabs.activate("jtabs-1");
41122  * </code></pre>
41123  * @constructor
41124  * Create a new TabPanel.
41125  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41126  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41127  */
41128 Roo.bootstrap.panel.Tabs = function(config){
41129     /**
41130     * The container element for this TabPanel.
41131     * @type Roo.Element
41132     */
41133     this.el = Roo.get(config.el);
41134     delete config.el;
41135     if(config){
41136         if(typeof config == "boolean"){
41137             this.tabPosition = config ? "bottom" : "top";
41138         }else{
41139             Roo.apply(this, config);
41140         }
41141     }
41142     
41143     if(this.tabPosition == "bottom"){
41144         // if tabs are at the bottom = create the body first.
41145         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41146         this.el.addClass("roo-tabs-bottom");
41147     }
41148     // next create the tabs holders
41149     
41150     if (this.tabPosition == "west"){
41151         
41152         var reg = this.region; // fake it..
41153         while (reg) {
41154             if (!reg.mgr.parent) {
41155                 break;
41156             }
41157             reg = reg.mgr.parent.region;
41158         }
41159         Roo.log("got nest?");
41160         Roo.log(reg);
41161         if (reg.mgr.getRegion('west')) {
41162             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41163             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41164             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41165             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41166             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41167         
41168             
41169         }
41170         
41171         
41172     } else {
41173      
41174         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41175         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41176         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41177         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41178     }
41179     
41180     
41181     if(Roo.isIE){
41182         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41183     }
41184     
41185     // finally - if tabs are at the top, then create the body last..
41186     if(this.tabPosition != "bottom"){
41187         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41188          * @type Roo.Element
41189          */
41190         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41191         this.el.addClass("roo-tabs-top");
41192     }
41193     this.items = [];
41194
41195     this.bodyEl.setStyle("position", "relative");
41196
41197     this.active = null;
41198     this.activateDelegate = this.activate.createDelegate(this);
41199
41200     this.addEvents({
41201         /**
41202          * @event tabchange
41203          * Fires when the active tab changes
41204          * @param {Roo.TabPanel} this
41205          * @param {Roo.TabPanelItem} activePanel The new active tab
41206          */
41207         "tabchange": true,
41208         /**
41209          * @event beforetabchange
41210          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41211          * @param {Roo.TabPanel} this
41212          * @param {Object} e Set cancel to true on this object to cancel the tab change
41213          * @param {Roo.TabPanelItem} tab The tab being changed to
41214          */
41215         "beforetabchange" : true
41216     });
41217
41218     Roo.EventManager.onWindowResize(this.onResize, this);
41219     this.cpad = this.el.getPadding("lr");
41220     this.hiddenCount = 0;
41221
41222
41223     // toolbar on the tabbar support...
41224     if (this.toolbar) {
41225         alert("no toolbar support yet");
41226         this.toolbar  = false;
41227         /*
41228         var tcfg = this.toolbar;
41229         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41230         this.toolbar = new Roo.Toolbar(tcfg);
41231         if (Roo.isSafari) {
41232             var tbl = tcfg.container.child('table', true);
41233             tbl.setAttribute('width', '100%');
41234         }
41235         */
41236         
41237     }
41238    
41239
41240
41241     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41242 };
41243
41244 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41245     /*
41246      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41247      */
41248     tabPosition : "top",
41249     /*
41250      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41251      */
41252     currentTabWidth : 0,
41253     /*
41254      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41255      */
41256     minTabWidth : 40,
41257     /*
41258      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41259      */
41260     maxTabWidth : 250,
41261     /*
41262      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41263      */
41264     preferredTabWidth : 175,
41265     /*
41266      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41267      */
41268     resizeTabs : false,
41269     /*
41270      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41271      */
41272     monitorResize : true,
41273     /*
41274      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41275      */
41276     toolbar : false,  // set by caller..
41277     
41278     region : false, /// set by caller
41279     
41280     disableTooltips : true, // not used yet...
41281
41282     /**
41283      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41284      * @param {String} id The id of the div to use <b>or create</b>
41285      * @param {String} text The text for the tab
41286      * @param {String} content (optional) Content to put in the TabPanelItem body
41287      * @param {Boolean} closable (optional) True to create a close icon on the tab
41288      * @return {Roo.TabPanelItem} The created TabPanelItem
41289      */
41290     addTab : function(id, text, content, closable, tpl)
41291     {
41292         var item = new Roo.bootstrap.panel.TabItem({
41293             panel: this,
41294             id : id,
41295             text : text,
41296             closable : closable,
41297             tpl : tpl
41298         });
41299         this.addTabItem(item);
41300         if(content){
41301             item.setContent(content);
41302         }
41303         return item;
41304     },
41305
41306     /**
41307      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41308      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41309      * @return {Roo.TabPanelItem}
41310      */
41311     getTab : function(id){
41312         return this.items[id];
41313     },
41314
41315     /**
41316      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41317      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41318      */
41319     hideTab : function(id){
41320         var t = this.items[id];
41321         if(!t.isHidden()){
41322            t.setHidden(true);
41323            this.hiddenCount++;
41324            this.autoSizeTabs();
41325         }
41326     },
41327
41328     /**
41329      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41330      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41331      */
41332     unhideTab : function(id){
41333         var t = this.items[id];
41334         if(t.isHidden()){
41335            t.setHidden(false);
41336            this.hiddenCount--;
41337            this.autoSizeTabs();
41338         }
41339     },
41340
41341     /**
41342      * Adds an existing {@link Roo.TabPanelItem}.
41343      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41344      */
41345     addTabItem : function(item)
41346     {
41347         this.items[item.id] = item;
41348         this.items.push(item);
41349         this.autoSizeTabs();
41350       //  if(this.resizeTabs){
41351     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41352   //         this.autoSizeTabs();
41353 //        }else{
41354 //            item.autoSize();
41355        // }
41356     },
41357
41358     /**
41359      * Removes a {@link Roo.TabPanelItem}.
41360      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41361      */
41362     removeTab : function(id){
41363         var items = this.items;
41364         var tab = items[id];
41365         if(!tab) { return; }
41366         var index = items.indexOf(tab);
41367         if(this.active == tab && items.length > 1){
41368             var newTab = this.getNextAvailable(index);
41369             if(newTab) {
41370                 newTab.activate();
41371             }
41372         }
41373         this.stripEl.dom.removeChild(tab.pnode.dom);
41374         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41375             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41376         }
41377         items.splice(index, 1);
41378         delete this.items[tab.id];
41379         tab.fireEvent("close", tab);
41380         tab.purgeListeners();
41381         this.autoSizeTabs();
41382     },
41383
41384     getNextAvailable : function(start){
41385         var items = this.items;
41386         var index = start;
41387         // look for a next tab that will slide over to
41388         // replace the one being removed
41389         while(index < items.length){
41390             var item = items[++index];
41391             if(item && !item.isHidden()){
41392                 return item;
41393             }
41394         }
41395         // if one isn't found select the previous tab (on the left)
41396         index = start;
41397         while(index >= 0){
41398             var item = items[--index];
41399             if(item && !item.isHidden()){
41400                 return item;
41401             }
41402         }
41403         return null;
41404     },
41405
41406     /**
41407      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41408      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41409      */
41410     disableTab : function(id){
41411         var tab = this.items[id];
41412         if(tab && this.active != tab){
41413             tab.disable();
41414         }
41415     },
41416
41417     /**
41418      * Enables a {@link Roo.TabPanelItem} that is disabled.
41419      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41420      */
41421     enableTab : function(id){
41422         var tab = this.items[id];
41423         tab.enable();
41424     },
41425
41426     /**
41427      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41428      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41429      * @return {Roo.TabPanelItem} The TabPanelItem.
41430      */
41431     activate : function(id)
41432     {
41433         //Roo.log('activite:'  + id);
41434         
41435         var tab = this.items[id];
41436         if(!tab){
41437             return null;
41438         }
41439         if(tab == this.active || tab.disabled){
41440             return tab;
41441         }
41442         var e = {};
41443         this.fireEvent("beforetabchange", this, e, tab);
41444         if(e.cancel !== true && !tab.disabled){
41445             if(this.active){
41446                 this.active.hide();
41447             }
41448             this.active = this.items[id];
41449             this.active.show();
41450             this.fireEvent("tabchange", this, this.active);
41451         }
41452         return tab;
41453     },
41454
41455     /**
41456      * Gets the active {@link Roo.TabPanelItem}.
41457      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41458      */
41459     getActiveTab : function(){
41460         return this.active;
41461     },
41462
41463     /**
41464      * Updates the tab body element to fit the height of the container element
41465      * for overflow scrolling
41466      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41467      */
41468     syncHeight : function(targetHeight){
41469         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41470         var bm = this.bodyEl.getMargins();
41471         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41472         this.bodyEl.setHeight(newHeight);
41473         return newHeight;
41474     },
41475
41476     onResize : function(){
41477         if(this.monitorResize){
41478             this.autoSizeTabs();
41479         }
41480     },
41481
41482     /**
41483      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41484      */
41485     beginUpdate : function(){
41486         this.updating = true;
41487     },
41488
41489     /**
41490      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41491      */
41492     endUpdate : function(){
41493         this.updating = false;
41494         this.autoSizeTabs();
41495     },
41496
41497     /**
41498      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41499      */
41500     autoSizeTabs : function()
41501     {
41502         var count = this.items.length;
41503         var vcount = count - this.hiddenCount;
41504         
41505         if (vcount < 2) {
41506             this.stripEl.hide();
41507         } else {
41508             this.stripEl.show();
41509         }
41510         
41511         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41512             return;
41513         }
41514         
41515         
41516         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41517         var availWidth = Math.floor(w / vcount);
41518         var b = this.stripBody;
41519         if(b.getWidth() > w){
41520             var tabs = this.items;
41521             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41522             if(availWidth < this.minTabWidth){
41523                 /*if(!this.sleft){    // incomplete scrolling code
41524                     this.createScrollButtons();
41525                 }
41526                 this.showScroll();
41527                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41528             }
41529         }else{
41530             if(this.currentTabWidth < this.preferredTabWidth){
41531                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41532             }
41533         }
41534     },
41535
41536     /**
41537      * Returns the number of tabs in this TabPanel.
41538      * @return {Number}
41539      */
41540      getCount : function(){
41541          return this.items.length;
41542      },
41543
41544     /**
41545      * Resizes all the tabs to the passed width
41546      * @param {Number} The new width
41547      */
41548     setTabWidth : function(width){
41549         this.currentTabWidth = width;
41550         for(var i = 0, len = this.items.length; i < len; i++) {
41551                 if(!this.items[i].isHidden()) {
41552                 this.items[i].setWidth(width);
41553             }
41554         }
41555     },
41556
41557     /**
41558      * Destroys this TabPanel
41559      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41560      */
41561     destroy : function(removeEl){
41562         Roo.EventManager.removeResizeListener(this.onResize, this);
41563         for(var i = 0, len = this.items.length; i < len; i++){
41564             this.items[i].purgeListeners();
41565         }
41566         if(removeEl === true){
41567             this.el.update("");
41568             this.el.remove();
41569         }
41570     },
41571     
41572     createStrip : function(container)
41573     {
41574         var strip = document.createElement("nav");
41575         strip.className = Roo.bootstrap.version == 4 ?
41576             "navbar-light bg-light" : 
41577             "navbar navbar-default"; //"x-tabs-wrap";
41578         container.appendChild(strip);
41579         return strip;
41580     },
41581     
41582     createStripList : function(strip)
41583     {
41584         // div wrapper for retard IE
41585         // returns the "tr" element.
41586         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41587         //'<div class="x-tabs-strip-wrap">'+
41588           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41589           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41590         return strip.firstChild; //.firstChild.firstChild.firstChild;
41591     },
41592     createBody : function(container)
41593     {
41594         var body = document.createElement("div");
41595         Roo.id(body, "tab-body");
41596         //Roo.fly(body).addClass("x-tabs-body");
41597         Roo.fly(body).addClass("tab-content");
41598         container.appendChild(body);
41599         return body;
41600     },
41601     createItemBody :function(bodyEl, id){
41602         var body = Roo.getDom(id);
41603         if(!body){
41604             body = document.createElement("div");
41605             body.id = id;
41606         }
41607         //Roo.fly(body).addClass("x-tabs-item-body");
41608         Roo.fly(body).addClass("tab-pane");
41609          bodyEl.insertBefore(body, bodyEl.firstChild);
41610         return body;
41611     },
41612     /** @private */
41613     createStripElements :  function(stripEl, text, closable, tpl)
41614     {
41615         var td = document.createElement("li"); // was td..
41616         td.className = 'nav-item';
41617         
41618         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41619         
41620         
41621         stripEl.appendChild(td);
41622         /*if(closable){
41623             td.className = "x-tabs-closable";
41624             if(!this.closeTpl){
41625                 this.closeTpl = new Roo.Template(
41626                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41627                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41628                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41629                 );
41630             }
41631             var el = this.closeTpl.overwrite(td, {"text": text});
41632             var close = el.getElementsByTagName("div")[0];
41633             var inner = el.getElementsByTagName("em")[0];
41634             return {"el": el, "close": close, "inner": inner};
41635         } else {
41636         */
41637         // not sure what this is..
41638 //            if(!this.tabTpl){
41639                 //this.tabTpl = new Roo.Template(
41640                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41641                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41642                 //);
41643 //                this.tabTpl = new Roo.Template(
41644 //                   '<a href="#">' +
41645 //                   '<span unselectable="on"' +
41646 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41647 //                            ' >{text}</span></a>'
41648 //                );
41649 //                
41650 //            }
41651
41652
41653             var template = tpl || this.tabTpl || false;
41654             
41655             if(!template){
41656                 template =  new Roo.Template(
41657                         Roo.bootstrap.version == 4 ? 
41658                             (
41659                                 '<a class="nav-link" href="#" unselectable="on"' +
41660                                      (this.disableTooltips ? '' : ' title="{text}"') +
41661                                      ' >{text}</a>'
41662                             ) : (
41663                                 '<a class="nav-link" href="#">' +
41664                                 '<span unselectable="on"' +
41665                                          (this.disableTooltips ? '' : ' title="{text}"') +
41666                                     ' >{text}</span></a>'
41667                             )
41668                 );
41669             }
41670             
41671             switch (typeof(template)) {
41672                 case 'object' :
41673                     break;
41674                 case 'string' :
41675                     template = new Roo.Template(template);
41676                     break;
41677                 default :
41678                     break;
41679             }
41680             
41681             var el = template.overwrite(td, {"text": text});
41682             
41683             var inner = el.getElementsByTagName("span")[0];
41684             
41685             return {"el": el, "inner": inner};
41686             
41687     }
41688         
41689     
41690 });
41691
41692 /**
41693  * @class Roo.TabPanelItem
41694  * @extends Roo.util.Observable
41695  * Represents an individual item (tab plus body) in a TabPanel.
41696  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41697  * @param {String} id The id of this TabPanelItem
41698  * @param {String} text The text for the tab of this TabPanelItem
41699  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41700  */
41701 Roo.bootstrap.panel.TabItem = function(config){
41702     /**
41703      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41704      * @type Roo.TabPanel
41705      */
41706     this.tabPanel = config.panel;
41707     /**
41708      * The id for this TabPanelItem
41709      * @type String
41710      */
41711     this.id = config.id;
41712     /** @private */
41713     this.disabled = false;
41714     /** @private */
41715     this.text = config.text;
41716     /** @private */
41717     this.loaded = false;
41718     this.closable = config.closable;
41719
41720     /**
41721      * The body element for this TabPanelItem.
41722      * @type Roo.Element
41723      */
41724     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41725     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41726     this.bodyEl.setStyle("display", "block");
41727     this.bodyEl.setStyle("zoom", "1");
41728     //this.hideAction();
41729
41730     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41731     /** @private */
41732     this.el = Roo.get(els.el);
41733     this.inner = Roo.get(els.inner, true);
41734      this.textEl = Roo.bootstrap.version == 4 ?
41735         this.el : Roo.get(this.el.dom.firstChild, true);
41736
41737     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41738     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41739
41740     
41741 //    this.el.on("mousedown", this.onTabMouseDown, this);
41742     this.el.on("click", this.onTabClick, this);
41743     /** @private */
41744     if(config.closable){
41745         var c = Roo.get(els.close, true);
41746         c.dom.title = this.closeText;
41747         c.addClassOnOver("close-over");
41748         c.on("click", this.closeClick, this);
41749      }
41750
41751     this.addEvents({
41752          /**
41753          * @event activate
41754          * Fires when this tab becomes the active tab.
41755          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41756          * @param {Roo.TabPanelItem} this
41757          */
41758         "activate": true,
41759         /**
41760          * @event beforeclose
41761          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41762          * @param {Roo.TabPanelItem} this
41763          * @param {Object} e Set cancel to true on this object to cancel the close.
41764          */
41765         "beforeclose": true,
41766         /**
41767          * @event close
41768          * Fires when this tab is closed.
41769          * @param {Roo.TabPanelItem} this
41770          */
41771          "close": true,
41772         /**
41773          * @event deactivate
41774          * Fires when this tab is no longer the active tab.
41775          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41776          * @param {Roo.TabPanelItem} this
41777          */
41778          "deactivate" : true
41779     });
41780     this.hidden = false;
41781
41782     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41783 };
41784
41785 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41786            {
41787     purgeListeners : function(){
41788        Roo.util.Observable.prototype.purgeListeners.call(this);
41789        this.el.removeAllListeners();
41790     },
41791     /**
41792      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41793      */
41794     show : function(){
41795         this.status_node.addClass("active");
41796         this.showAction();
41797         if(Roo.isOpera){
41798             this.tabPanel.stripWrap.repaint();
41799         }
41800         this.fireEvent("activate", this.tabPanel, this);
41801     },
41802
41803     /**
41804      * Returns true if this tab is the active tab.
41805      * @return {Boolean}
41806      */
41807     isActive : function(){
41808         return this.tabPanel.getActiveTab() == this;
41809     },
41810
41811     /**
41812      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41813      */
41814     hide : function(){
41815         this.status_node.removeClass("active");
41816         this.hideAction();
41817         this.fireEvent("deactivate", this.tabPanel, this);
41818     },
41819
41820     hideAction : function(){
41821         this.bodyEl.hide();
41822         this.bodyEl.setStyle("position", "absolute");
41823         this.bodyEl.setLeft("-20000px");
41824         this.bodyEl.setTop("-20000px");
41825     },
41826
41827     showAction : function(){
41828         this.bodyEl.setStyle("position", "relative");
41829         this.bodyEl.setTop("");
41830         this.bodyEl.setLeft("");
41831         this.bodyEl.show();
41832     },
41833
41834     /**
41835      * Set the tooltip for the tab.
41836      * @param {String} tooltip The tab's tooltip
41837      */
41838     setTooltip : function(text){
41839         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41840             this.textEl.dom.qtip = text;
41841             this.textEl.dom.removeAttribute('title');
41842         }else{
41843             this.textEl.dom.title = text;
41844         }
41845     },
41846
41847     onTabClick : function(e){
41848         e.preventDefault();
41849         this.tabPanel.activate(this.id);
41850     },
41851
41852     onTabMouseDown : function(e){
41853         e.preventDefault();
41854         this.tabPanel.activate(this.id);
41855     },
41856 /*
41857     getWidth : function(){
41858         return this.inner.getWidth();
41859     },
41860
41861     setWidth : function(width){
41862         var iwidth = width - this.linode.getPadding("lr");
41863         this.inner.setWidth(iwidth);
41864         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41865         this.linode.setWidth(width);
41866     },
41867 */
41868     /**
41869      * Show or hide the tab
41870      * @param {Boolean} hidden True to hide or false to show.
41871      */
41872     setHidden : function(hidden){
41873         this.hidden = hidden;
41874         this.linode.setStyle("display", hidden ? "none" : "");
41875     },
41876
41877     /**
41878      * Returns true if this tab is "hidden"
41879      * @return {Boolean}
41880      */
41881     isHidden : function(){
41882         return this.hidden;
41883     },
41884
41885     /**
41886      * Returns the text for this tab
41887      * @return {String}
41888      */
41889     getText : function(){
41890         return this.text;
41891     },
41892     /*
41893     autoSize : function(){
41894         //this.el.beginMeasure();
41895         this.textEl.setWidth(1);
41896         /*
41897          *  #2804 [new] Tabs in Roojs
41898          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41899          */
41900         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41901         //this.el.endMeasure();
41902     //},
41903
41904     /**
41905      * Sets the text for the tab (Note: this also sets the tooltip text)
41906      * @param {String} text The tab's text and tooltip
41907      */
41908     setText : function(text){
41909         this.text = text;
41910         this.textEl.update(text);
41911         this.setTooltip(text);
41912         //if(!this.tabPanel.resizeTabs){
41913         //    this.autoSize();
41914         //}
41915     },
41916     /**
41917      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41918      */
41919     activate : function(){
41920         this.tabPanel.activate(this.id);
41921     },
41922
41923     /**
41924      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41925      */
41926     disable : function(){
41927         if(this.tabPanel.active != this){
41928             this.disabled = true;
41929             this.status_node.addClass("disabled");
41930         }
41931     },
41932
41933     /**
41934      * Enables this TabPanelItem if it was previously disabled.
41935      */
41936     enable : function(){
41937         this.disabled = false;
41938         this.status_node.removeClass("disabled");
41939     },
41940
41941     /**
41942      * Sets the content for this TabPanelItem.
41943      * @param {String} content The content
41944      * @param {Boolean} loadScripts true to look for and load scripts
41945      */
41946     setContent : function(content, loadScripts){
41947         this.bodyEl.update(content, loadScripts);
41948     },
41949
41950     /**
41951      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41952      * @return {Roo.UpdateManager} The UpdateManager
41953      */
41954     getUpdateManager : function(){
41955         return this.bodyEl.getUpdateManager();
41956     },
41957
41958     /**
41959      * Set a URL to be used to load the content for this TabPanelItem.
41960      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41961      * @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)
41962      * @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)
41963      * @return {Roo.UpdateManager} The UpdateManager
41964      */
41965     setUrl : function(url, params, loadOnce){
41966         if(this.refreshDelegate){
41967             this.un('activate', this.refreshDelegate);
41968         }
41969         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41970         this.on("activate", this.refreshDelegate);
41971         return this.bodyEl.getUpdateManager();
41972     },
41973
41974     /** @private */
41975     _handleRefresh : function(url, params, loadOnce){
41976         if(!loadOnce || !this.loaded){
41977             var updater = this.bodyEl.getUpdateManager();
41978             updater.update(url, params, this._setLoaded.createDelegate(this));
41979         }
41980     },
41981
41982     /**
41983      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41984      *   Will fail silently if the setUrl method has not been called.
41985      *   This does not activate the panel, just updates its content.
41986      */
41987     refresh : function(){
41988         if(this.refreshDelegate){
41989            this.loaded = false;
41990            this.refreshDelegate();
41991         }
41992     },
41993
41994     /** @private */
41995     _setLoaded : function(){
41996         this.loaded = true;
41997     },
41998
41999     /** @private */
42000     closeClick : function(e){
42001         var o = {};
42002         e.stopEvent();
42003         this.fireEvent("beforeclose", this, o);
42004         if(o.cancel !== true){
42005             this.tabPanel.removeTab(this.id);
42006         }
42007     },
42008     /**
42009      * The text displayed in the tooltip for the close icon.
42010      * @type String
42011      */
42012     closeText : "Close this tab"
42013 });
42014 /**
42015 *    This script refer to:
42016 *    Title: International Telephone Input
42017 *    Author: Jack O'Connor
42018 *    Code version:  v12.1.12
42019 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42020 **/
42021
42022 Roo.bootstrap.form.PhoneInputData = function() {
42023     var d = [
42024       [
42025         "Afghanistan (‫افغانستان‬‎)",
42026         "af",
42027         "93"
42028       ],
42029       [
42030         "Albania (Shqipëri)",
42031         "al",
42032         "355"
42033       ],
42034       [
42035         "Algeria (‫الجزائر‬‎)",
42036         "dz",
42037         "213"
42038       ],
42039       [
42040         "American Samoa",
42041         "as",
42042         "1684"
42043       ],
42044       [
42045         "Andorra",
42046         "ad",
42047         "376"
42048       ],
42049       [
42050         "Angola",
42051         "ao",
42052         "244"
42053       ],
42054       [
42055         "Anguilla",
42056         "ai",
42057         "1264"
42058       ],
42059       [
42060         "Antigua and Barbuda",
42061         "ag",
42062         "1268"
42063       ],
42064       [
42065         "Argentina",
42066         "ar",
42067         "54"
42068       ],
42069       [
42070         "Armenia (Հայաստան)",
42071         "am",
42072         "374"
42073       ],
42074       [
42075         "Aruba",
42076         "aw",
42077         "297"
42078       ],
42079       [
42080         "Australia",
42081         "au",
42082         "61",
42083         0
42084       ],
42085       [
42086         "Austria (Österreich)",
42087         "at",
42088         "43"
42089       ],
42090       [
42091         "Azerbaijan (Azərbaycan)",
42092         "az",
42093         "994"
42094       ],
42095       [
42096         "Bahamas",
42097         "bs",
42098         "1242"
42099       ],
42100       [
42101         "Bahrain (‫البحرين‬‎)",
42102         "bh",
42103         "973"
42104       ],
42105       [
42106         "Bangladesh (বাংলাদেশ)",
42107         "bd",
42108         "880"
42109       ],
42110       [
42111         "Barbados",
42112         "bb",
42113         "1246"
42114       ],
42115       [
42116         "Belarus (Беларусь)",
42117         "by",
42118         "375"
42119       ],
42120       [
42121         "Belgium (België)",
42122         "be",
42123         "32"
42124       ],
42125       [
42126         "Belize",
42127         "bz",
42128         "501"
42129       ],
42130       [
42131         "Benin (Bénin)",
42132         "bj",
42133         "229"
42134       ],
42135       [
42136         "Bermuda",
42137         "bm",
42138         "1441"
42139       ],
42140       [
42141         "Bhutan (འབྲུག)",
42142         "bt",
42143         "975"
42144       ],
42145       [
42146         "Bolivia",
42147         "bo",
42148         "591"
42149       ],
42150       [
42151         "Bosnia and Herzegovina (Босна и Херцеговина)",
42152         "ba",
42153         "387"
42154       ],
42155       [
42156         "Botswana",
42157         "bw",
42158         "267"
42159       ],
42160       [
42161         "Brazil (Brasil)",
42162         "br",
42163         "55"
42164       ],
42165       [
42166         "British Indian Ocean Territory",
42167         "io",
42168         "246"
42169       ],
42170       [
42171         "British Virgin Islands",
42172         "vg",
42173         "1284"
42174       ],
42175       [
42176         "Brunei",
42177         "bn",
42178         "673"
42179       ],
42180       [
42181         "Bulgaria (България)",
42182         "bg",
42183         "359"
42184       ],
42185       [
42186         "Burkina Faso",
42187         "bf",
42188         "226"
42189       ],
42190       [
42191         "Burundi (Uburundi)",
42192         "bi",
42193         "257"
42194       ],
42195       [
42196         "Cambodia (កម្ពុជា)",
42197         "kh",
42198         "855"
42199       ],
42200       [
42201         "Cameroon (Cameroun)",
42202         "cm",
42203         "237"
42204       ],
42205       [
42206         "Canada",
42207         "ca",
42208         "1",
42209         1,
42210         ["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"]
42211       ],
42212       [
42213         "Cape Verde (Kabu Verdi)",
42214         "cv",
42215         "238"
42216       ],
42217       [
42218         "Caribbean Netherlands",
42219         "bq",
42220         "599",
42221         1
42222       ],
42223       [
42224         "Cayman Islands",
42225         "ky",
42226         "1345"
42227       ],
42228       [
42229         "Central African Republic (République centrafricaine)",
42230         "cf",
42231         "236"
42232       ],
42233       [
42234         "Chad (Tchad)",
42235         "td",
42236         "235"
42237       ],
42238       [
42239         "Chile",
42240         "cl",
42241         "56"
42242       ],
42243       [
42244         "China (中国)",
42245         "cn",
42246         "86"
42247       ],
42248       [
42249         "Christmas Island",
42250         "cx",
42251         "61",
42252         2
42253       ],
42254       [
42255         "Cocos (Keeling) Islands",
42256         "cc",
42257         "61",
42258         1
42259       ],
42260       [
42261         "Colombia",
42262         "co",
42263         "57"
42264       ],
42265       [
42266         "Comoros (‫جزر القمر‬‎)",
42267         "km",
42268         "269"
42269       ],
42270       [
42271         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42272         "cd",
42273         "243"
42274       ],
42275       [
42276         "Congo (Republic) (Congo-Brazzaville)",
42277         "cg",
42278         "242"
42279       ],
42280       [
42281         "Cook Islands",
42282         "ck",
42283         "682"
42284       ],
42285       [
42286         "Costa Rica",
42287         "cr",
42288         "506"
42289       ],
42290       [
42291         "Côte d’Ivoire",
42292         "ci",
42293         "225"
42294       ],
42295       [
42296         "Croatia (Hrvatska)",
42297         "hr",
42298         "385"
42299       ],
42300       [
42301         "Cuba",
42302         "cu",
42303         "53"
42304       ],
42305       [
42306         "Curaçao",
42307         "cw",
42308         "599",
42309         0
42310       ],
42311       [
42312         "Cyprus (Κύπρος)",
42313         "cy",
42314         "357"
42315       ],
42316       [
42317         "Czech Republic (Česká republika)",
42318         "cz",
42319         "420"
42320       ],
42321       [
42322         "Denmark (Danmark)",
42323         "dk",
42324         "45"
42325       ],
42326       [
42327         "Djibouti",
42328         "dj",
42329         "253"
42330       ],
42331       [
42332         "Dominica",
42333         "dm",
42334         "1767"
42335       ],
42336       [
42337         "Dominican Republic (República Dominicana)",
42338         "do",
42339         "1",
42340         2,
42341         ["809", "829", "849"]
42342       ],
42343       [
42344         "Ecuador",
42345         "ec",
42346         "593"
42347       ],
42348       [
42349         "Egypt (‫مصر‬‎)",
42350         "eg",
42351         "20"
42352       ],
42353       [
42354         "El Salvador",
42355         "sv",
42356         "503"
42357       ],
42358       [
42359         "Equatorial Guinea (Guinea Ecuatorial)",
42360         "gq",
42361         "240"
42362       ],
42363       [
42364         "Eritrea",
42365         "er",
42366         "291"
42367       ],
42368       [
42369         "Estonia (Eesti)",
42370         "ee",
42371         "372"
42372       ],
42373       [
42374         "Ethiopia",
42375         "et",
42376         "251"
42377       ],
42378       [
42379         "Falkland Islands (Islas Malvinas)",
42380         "fk",
42381         "500"
42382       ],
42383       [
42384         "Faroe Islands (Føroyar)",
42385         "fo",
42386         "298"
42387       ],
42388       [
42389         "Fiji",
42390         "fj",
42391         "679"
42392       ],
42393       [
42394         "Finland (Suomi)",
42395         "fi",
42396         "358",
42397         0
42398       ],
42399       [
42400         "France",
42401         "fr",
42402         "33"
42403       ],
42404       [
42405         "French Guiana (Guyane française)",
42406         "gf",
42407         "594"
42408       ],
42409       [
42410         "French Polynesia (Polynésie française)",
42411         "pf",
42412         "689"
42413       ],
42414       [
42415         "Gabon",
42416         "ga",
42417         "241"
42418       ],
42419       [
42420         "Gambia",
42421         "gm",
42422         "220"
42423       ],
42424       [
42425         "Georgia (საქართველო)",
42426         "ge",
42427         "995"
42428       ],
42429       [
42430         "Germany (Deutschland)",
42431         "de",
42432         "49"
42433       ],
42434       [
42435         "Ghana (Gaana)",
42436         "gh",
42437         "233"
42438       ],
42439       [
42440         "Gibraltar",
42441         "gi",
42442         "350"
42443       ],
42444       [
42445         "Greece (Ελλάδα)",
42446         "gr",
42447         "30"
42448       ],
42449       [
42450         "Greenland (Kalaallit Nunaat)",
42451         "gl",
42452         "299"
42453       ],
42454       [
42455         "Grenada",
42456         "gd",
42457         "1473"
42458       ],
42459       [
42460         "Guadeloupe",
42461         "gp",
42462         "590",
42463         0
42464       ],
42465       [
42466         "Guam",
42467         "gu",
42468         "1671"
42469       ],
42470       [
42471         "Guatemala",
42472         "gt",
42473         "502"
42474       ],
42475       [
42476         "Guernsey",
42477         "gg",
42478         "44",
42479         1
42480       ],
42481       [
42482         "Guinea (Guinée)",
42483         "gn",
42484         "224"
42485       ],
42486       [
42487         "Guinea-Bissau (Guiné Bissau)",
42488         "gw",
42489         "245"
42490       ],
42491       [
42492         "Guyana",
42493         "gy",
42494         "592"
42495       ],
42496       [
42497         "Haiti",
42498         "ht",
42499         "509"
42500       ],
42501       [
42502         "Honduras",
42503         "hn",
42504         "504"
42505       ],
42506       [
42507         "Hong Kong (香港)",
42508         "hk",
42509         "852"
42510       ],
42511       [
42512         "Hungary (Magyarország)",
42513         "hu",
42514         "36"
42515       ],
42516       [
42517         "Iceland (Ísland)",
42518         "is",
42519         "354"
42520       ],
42521       [
42522         "India (भारत)",
42523         "in",
42524         "91"
42525       ],
42526       [
42527         "Indonesia",
42528         "id",
42529         "62"
42530       ],
42531       [
42532         "Iran (‫ایران‬‎)",
42533         "ir",
42534         "98"
42535       ],
42536       [
42537         "Iraq (‫العراق‬‎)",
42538         "iq",
42539         "964"
42540       ],
42541       [
42542         "Ireland",
42543         "ie",
42544         "353"
42545       ],
42546       [
42547         "Isle of Man",
42548         "im",
42549         "44",
42550         2
42551       ],
42552       [
42553         "Israel (‫ישראל‬‎)",
42554         "il",
42555         "972"
42556       ],
42557       [
42558         "Italy (Italia)",
42559         "it",
42560         "39",
42561         0
42562       ],
42563       [
42564         "Jamaica",
42565         "jm",
42566         "1876"
42567       ],
42568       [
42569         "Japan (日本)",
42570         "jp",
42571         "81"
42572       ],
42573       [
42574         "Jersey",
42575         "je",
42576         "44",
42577         3
42578       ],
42579       [
42580         "Jordan (‫الأردن‬‎)",
42581         "jo",
42582         "962"
42583       ],
42584       [
42585         "Kazakhstan (Казахстан)",
42586         "kz",
42587         "7",
42588         1
42589       ],
42590       [
42591         "Kenya",
42592         "ke",
42593         "254"
42594       ],
42595       [
42596         "Kiribati",
42597         "ki",
42598         "686"
42599       ],
42600       [
42601         "Kosovo",
42602         "xk",
42603         "383"
42604       ],
42605       [
42606         "Kuwait (‫الكويت‬‎)",
42607         "kw",
42608         "965"
42609       ],
42610       [
42611         "Kyrgyzstan (Кыргызстан)",
42612         "kg",
42613         "996"
42614       ],
42615       [
42616         "Laos (ລາວ)",
42617         "la",
42618         "856"
42619       ],
42620       [
42621         "Latvia (Latvija)",
42622         "lv",
42623         "371"
42624       ],
42625       [
42626         "Lebanon (‫لبنان‬‎)",
42627         "lb",
42628         "961"
42629       ],
42630       [
42631         "Lesotho",
42632         "ls",
42633         "266"
42634       ],
42635       [
42636         "Liberia",
42637         "lr",
42638         "231"
42639       ],
42640       [
42641         "Libya (‫ليبيا‬‎)",
42642         "ly",
42643         "218"
42644       ],
42645       [
42646         "Liechtenstein",
42647         "li",
42648         "423"
42649       ],
42650       [
42651         "Lithuania (Lietuva)",
42652         "lt",
42653         "370"
42654       ],
42655       [
42656         "Luxembourg",
42657         "lu",
42658         "352"
42659       ],
42660       [
42661         "Macau (澳門)",
42662         "mo",
42663         "853"
42664       ],
42665       [
42666         "Macedonia (FYROM) (Македонија)",
42667         "mk",
42668         "389"
42669       ],
42670       [
42671         "Madagascar (Madagasikara)",
42672         "mg",
42673         "261"
42674       ],
42675       [
42676         "Malawi",
42677         "mw",
42678         "265"
42679       ],
42680       [
42681         "Malaysia",
42682         "my",
42683         "60"
42684       ],
42685       [
42686         "Maldives",
42687         "mv",
42688         "960"
42689       ],
42690       [
42691         "Mali",
42692         "ml",
42693         "223"
42694       ],
42695       [
42696         "Malta",
42697         "mt",
42698         "356"
42699       ],
42700       [
42701         "Marshall Islands",
42702         "mh",
42703         "692"
42704       ],
42705       [
42706         "Martinique",
42707         "mq",
42708         "596"
42709       ],
42710       [
42711         "Mauritania (‫موريتانيا‬‎)",
42712         "mr",
42713         "222"
42714       ],
42715       [
42716         "Mauritius (Moris)",
42717         "mu",
42718         "230"
42719       ],
42720       [
42721         "Mayotte",
42722         "yt",
42723         "262",
42724         1
42725       ],
42726       [
42727         "Mexico (México)",
42728         "mx",
42729         "52"
42730       ],
42731       [
42732         "Micronesia",
42733         "fm",
42734         "691"
42735       ],
42736       [
42737         "Moldova (Republica Moldova)",
42738         "md",
42739         "373"
42740       ],
42741       [
42742         "Monaco",
42743         "mc",
42744         "377"
42745       ],
42746       [
42747         "Mongolia (Монгол)",
42748         "mn",
42749         "976"
42750       ],
42751       [
42752         "Montenegro (Crna Gora)",
42753         "me",
42754         "382"
42755       ],
42756       [
42757         "Montserrat",
42758         "ms",
42759         "1664"
42760       ],
42761       [
42762         "Morocco (‫المغرب‬‎)",
42763         "ma",
42764         "212",
42765         0
42766       ],
42767       [
42768         "Mozambique (Moçambique)",
42769         "mz",
42770         "258"
42771       ],
42772       [
42773         "Myanmar (Burma) (မြန်မာ)",
42774         "mm",
42775         "95"
42776       ],
42777       [
42778         "Namibia (Namibië)",
42779         "na",
42780         "264"
42781       ],
42782       [
42783         "Nauru",
42784         "nr",
42785         "674"
42786       ],
42787       [
42788         "Nepal (नेपाल)",
42789         "np",
42790         "977"
42791       ],
42792       [
42793         "Netherlands (Nederland)",
42794         "nl",
42795         "31"
42796       ],
42797       [
42798         "New Caledonia (Nouvelle-Calédonie)",
42799         "nc",
42800         "687"
42801       ],
42802       [
42803         "New Zealand",
42804         "nz",
42805         "64"
42806       ],
42807       [
42808         "Nicaragua",
42809         "ni",
42810         "505"
42811       ],
42812       [
42813         "Niger (Nijar)",
42814         "ne",
42815         "227"
42816       ],
42817       [
42818         "Nigeria",
42819         "ng",
42820         "234"
42821       ],
42822       [
42823         "Niue",
42824         "nu",
42825         "683"
42826       ],
42827       [
42828         "Norfolk Island",
42829         "nf",
42830         "672"
42831       ],
42832       [
42833         "North Korea (조선 민주주의 인민 공화국)",
42834         "kp",
42835         "850"
42836       ],
42837       [
42838         "Northern Mariana Islands",
42839         "mp",
42840         "1670"
42841       ],
42842       [
42843         "Norway (Norge)",
42844         "no",
42845         "47",
42846         0
42847       ],
42848       [
42849         "Oman (‫عُمان‬‎)",
42850         "om",
42851         "968"
42852       ],
42853       [
42854         "Pakistan (‫پاکستان‬‎)",
42855         "pk",
42856         "92"
42857       ],
42858       [
42859         "Palau",
42860         "pw",
42861         "680"
42862       ],
42863       [
42864         "Palestine (‫فلسطين‬‎)",
42865         "ps",
42866         "970"
42867       ],
42868       [
42869         "Panama (Panamá)",
42870         "pa",
42871         "507"
42872       ],
42873       [
42874         "Papua New Guinea",
42875         "pg",
42876         "675"
42877       ],
42878       [
42879         "Paraguay",
42880         "py",
42881         "595"
42882       ],
42883       [
42884         "Peru (Perú)",
42885         "pe",
42886         "51"
42887       ],
42888       [
42889         "Philippines",
42890         "ph",
42891         "63"
42892       ],
42893       [
42894         "Poland (Polska)",
42895         "pl",
42896         "48"
42897       ],
42898       [
42899         "Portugal",
42900         "pt",
42901         "351"
42902       ],
42903       [
42904         "Puerto Rico",
42905         "pr",
42906         "1",
42907         3,
42908         ["787", "939"]
42909       ],
42910       [
42911         "Qatar (‫قطر‬‎)",
42912         "qa",
42913         "974"
42914       ],
42915       [
42916         "Réunion (La Réunion)",
42917         "re",
42918         "262",
42919         0
42920       ],
42921       [
42922         "Romania (România)",
42923         "ro",
42924         "40"
42925       ],
42926       [
42927         "Russia (Россия)",
42928         "ru",
42929         "7",
42930         0
42931       ],
42932       [
42933         "Rwanda",
42934         "rw",
42935         "250"
42936       ],
42937       [
42938         "Saint Barthélemy",
42939         "bl",
42940         "590",
42941         1
42942       ],
42943       [
42944         "Saint Helena",
42945         "sh",
42946         "290"
42947       ],
42948       [
42949         "Saint Kitts and Nevis",
42950         "kn",
42951         "1869"
42952       ],
42953       [
42954         "Saint Lucia",
42955         "lc",
42956         "1758"
42957       ],
42958       [
42959         "Saint Martin (Saint-Martin (partie française))",
42960         "mf",
42961         "590",
42962         2
42963       ],
42964       [
42965         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42966         "pm",
42967         "508"
42968       ],
42969       [
42970         "Saint Vincent and the Grenadines",
42971         "vc",
42972         "1784"
42973       ],
42974       [
42975         "Samoa",
42976         "ws",
42977         "685"
42978       ],
42979       [
42980         "San Marino",
42981         "sm",
42982         "378"
42983       ],
42984       [
42985         "São Tomé and Príncipe (São Tomé e Príncipe)",
42986         "st",
42987         "239"
42988       ],
42989       [
42990         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42991         "sa",
42992         "966"
42993       ],
42994       [
42995         "Senegal (Sénégal)",
42996         "sn",
42997         "221"
42998       ],
42999       [
43000         "Serbia (Србија)",
43001         "rs",
43002         "381"
43003       ],
43004       [
43005         "Seychelles",
43006         "sc",
43007         "248"
43008       ],
43009       [
43010         "Sierra Leone",
43011         "sl",
43012         "232"
43013       ],
43014       [
43015         "Singapore",
43016         "sg",
43017         "65"
43018       ],
43019       [
43020         "Sint Maarten",
43021         "sx",
43022         "1721"
43023       ],
43024       [
43025         "Slovakia (Slovensko)",
43026         "sk",
43027         "421"
43028       ],
43029       [
43030         "Slovenia (Slovenija)",
43031         "si",
43032         "386"
43033       ],
43034       [
43035         "Solomon Islands",
43036         "sb",
43037         "677"
43038       ],
43039       [
43040         "Somalia (Soomaaliya)",
43041         "so",
43042         "252"
43043       ],
43044       [
43045         "South Africa",
43046         "za",
43047         "27"
43048       ],
43049       [
43050         "South Korea (대한민국)",
43051         "kr",
43052         "82"
43053       ],
43054       [
43055         "South Sudan (‫جنوب السودان‬‎)",
43056         "ss",
43057         "211"
43058       ],
43059       [
43060         "Spain (España)",
43061         "es",
43062         "34"
43063       ],
43064       [
43065         "Sri Lanka (ශ්‍රී ලංකාව)",
43066         "lk",
43067         "94"
43068       ],
43069       [
43070         "Sudan (‫السودان‬‎)",
43071         "sd",
43072         "249"
43073       ],
43074       [
43075         "Suriname",
43076         "sr",
43077         "597"
43078       ],
43079       [
43080         "Svalbard and Jan Mayen",
43081         "sj",
43082         "47",
43083         1
43084       ],
43085       [
43086         "Swaziland",
43087         "sz",
43088         "268"
43089       ],
43090       [
43091         "Sweden (Sverige)",
43092         "se",
43093         "46"
43094       ],
43095       [
43096         "Switzerland (Schweiz)",
43097         "ch",
43098         "41"
43099       ],
43100       [
43101         "Syria (‫سوريا‬‎)",
43102         "sy",
43103         "963"
43104       ],
43105       [
43106         "Taiwan (台灣)",
43107         "tw",
43108         "886"
43109       ],
43110       [
43111         "Tajikistan",
43112         "tj",
43113         "992"
43114       ],
43115       [
43116         "Tanzania",
43117         "tz",
43118         "255"
43119       ],
43120       [
43121         "Thailand (ไทย)",
43122         "th",
43123         "66"
43124       ],
43125       [
43126         "Timor-Leste",
43127         "tl",
43128         "670"
43129       ],
43130       [
43131         "Togo",
43132         "tg",
43133         "228"
43134       ],
43135       [
43136         "Tokelau",
43137         "tk",
43138         "690"
43139       ],
43140       [
43141         "Tonga",
43142         "to",
43143         "676"
43144       ],
43145       [
43146         "Trinidad and Tobago",
43147         "tt",
43148         "1868"
43149       ],
43150       [
43151         "Tunisia (‫تونس‬‎)",
43152         "tn",
43153         "216"
43154       ],
43155       [
43156         "Turkey (Türkiye)",
43157         "tr",
43158         "90"
43159       ],
43160       [
43161         "Turkmenistan",
43162         "tm",
43163         "993"
43164       ],
43165       [
43166         "Turks and Caicos Islands",
43167         "tc",
43168         "1649"
43169       ],
43170       [
43171         "Tuvalu",
43172         "tv",
43173         "688"
43174       ],
43175       [
43176         "U.S. Virgin Islands",
43177         "vi",
43178         "1340"
43179       ],
43180       [
43181         "Uganda",
43182         "ug",
43183         "256"
43184       ],
43185       [
43186         "Ukraine (Україна)",
43187         "ua",
43188         "380"
43189       ],
43190       [
43191         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43192         "ae",
43193         "971"
43194       ],
43195       [
43196         "United Kingdom",
43197         "gb",
43198         "44",
43199         0
43200       ],
43201       [
43202         "United States",
43203         "us",
43204         "1",
43205         0
43206       ],
43207       [
43208         "Uruguay",
43209         "uy",
43210         "598"
43211       ],
43212       [
43213         "Uzbekistan (Oʻzbekiston)",
43214         "uz",
43215         "998"
43216       ],
43217       [
43218         "Vanuatu",
43219         "vu",
43220         "678"
43221       ],
43222       [
43223         "Vatican City (Città del Vaticano)",
43224         "va",
43225         "39",
43226         1
43227       ],
43228       [
43229         "Venezuela",
43230         "ve",
43231         "58"
43232       ],
43233       [
43234         "Vietnam (Việt Nam)",
43235         "vn",
43236         "84"
43237       ],
43238       [
43239         "Wallis and Futuna (Wallis-et-Futuna)",
43240         "wf",
43241         "681"
43242       ],
43243       [
43244         "Western Sahara (‫الصحراء الغربية‬‎)",
43245         "eh",
43246         "212",
43247         1
43248       ],
43249       [
43250         "Yemen (‫اليمن‬‎)",
43251         "ye",
43252         "967"
43253       ],
43254       [
43255         "Zambia",
43256         "zm",
43257         "260"
43258       ],
43259       [
43260         "Zimbabwe",
43261         "zw",
43262         "263"
43263       ],
43264       [
43265         "Åland Islands",
43266         "ax",
43267         "358",
43268         1
43269       ]
43270   ];
43271   
43272   return d;
43273 }/**
43274 *    This script refer to:
43275 *    Title: International Telephone Input
43276 *    Author: Jack O'Connor
43277 *    Code version:  v12.1.12
43278 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43279 **/
43280
43281 /**
43282  * @class Roo.bootstrap.form.PhoneInput
43283  * @extends Roo.bootstrap.form.TriggerField
43284  * An input with International dial-code selection
43285  
43286  * @cfg {String} defaultDialCode default '+852'
43287  * @cfg {Array} preferedCountries default []
43288   
43289  * @constructor
43290  * Create a new PhoneInput.
43291  * @param {Object} config Configuration options
43292  */
43293
43294 Roo.bootstrap.form.PhoneInput = function(config) {
43295     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43296 };
43297
43298 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43299         /**
43300         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43301         */
43302         listWidth: undefined,
43303         
43304         selectedClass: 'active',
43305         
43306         invalidClass : "has-warning",
43307         
43308         validClass: 'has-success',
43309         
43310         allowed: '0123456789',
43311         
43312         max_length: 15,
43313         
43314         /**
43315          * @cfg {String} defaultDialCode The default dial code when initializing the input
43316          */
43317         defaultDialCode: '+852',
43318         
43319         /**
43320          * @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
43321          */
43322         preferedCountries: false,
43323         
43324         getAutoCreate : function()
43325         {
43326             var data = Roo.bootstrap.form.PhoneInputData();
43327             var align = this.labelAlign || this.parentLabelAlign();
43328             var id = Roo.id();
43329             
43330             this.allCountries = [];
43331             this.dialCodeMapping = [];
43332             
43333             for (var i = 0; i < data.length; i++) {
43334               var c = data[i];
43335               this.allCountries[i] = {
43336                 name: c[0],
43337                 iso2: c[1],
43338                 dialCode: c[2],
43339                 priority: c[3] || 0,
43340                 areaCodes: c[4] || null
43341               };
43342               this.dialCodeMapping[c[2]] = {
43343                   name: c[0],
43344                   iso2: c[1],
43345                   priority: c[3] || 0,
43346                   areaCodes: c[4] || null
43347               };
43348             }
43349             
43350             var cfg = {
43351                 cls: 'form-group',
43352                 cn: []
43353             };
43354             
43355             var input =  {
43356                 tag: 'input',
43357                 id : id,
43358                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43359                 maxlength: this.max_length,
43360                 cls : 'form-control tel-input',
43361                 autocomplete: 'new-password'
43362             };
43363             
43364             var hiddenInput = {
43365                 tag: 'input',
43366                 type: 'hidden',
43367                 cls: 'hidden-tel-input'
43368             };
43369             
43370             if (this.name) {
43371                 hiddenInput.name = this.name;
43372             }
43373             
43374             if (this.disabled) {
43375                 input.disabled = true;
43376             }
43377             
43378             var flag_container = {
43379                 tag: 'div',
43380                 cls: 'flag-box',
43381                 cn: [
43382                     {
43383                         tag: 'div',
43384                         cls: 'flag'
43385                     },
43386                     {
43387                         tag: 'div',
43388                         cls: 'caret'
43389                     }
43390                 ]
43391             };
43392             
43393             var box = {
43394                 tag: 'div',
43395                 cls: this.hasFeedback ? 'has-feedback' : '',
43396                 cn: [
43397                     hiddenInput,
43398                     input,
43399                     {
43400                         tag: 'input',
43401                         cls: 'dial-code-holder',
43402                         disabled: true
43403                     }
43404                 ]
43405             };
43406             
43407             var container = {
43408                 cls: 'roo-select2-container input-group',
43409                 cn: [
43410                     flag_container,
43411                     box
43412                 ]
43413             };
43414             
43415             if (this.fieldLabel.length) {
43416                 var indicator = {
43417                     tag: 'i',
43418                     tooltip: 'This field is required'
43419                 };
43420                 
43421                 var label = {
43422                     tag: 'label',
43423                     'for':  id,
43424                     cls: 'control-label',
43425                     cn: []
43426                 };
43427                 
43428                 var label_text = {
43429                     tag: 'span',
43430                     html: this.fieldLabel
43431                 };
43432                 
43433                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43434                 label.cn = [
43435                     indicator,
43436                     label_text
43437                 ];
43438                 
43439                 if(this.indicatorpos == 'right') {
43440                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43441                     label.cn = [
43442                         label_text,
43443                         indicator
43444                     ];
43445                 }
43446                 
43447                 if(align == 'left') {
43448                     container = {
43449                         tag: 'div',
43450                         cn: [
43451                             container
43452                         ]
43453                     };
43454                     
43455                     if(this.labelWidth > 12){
43456                         label.style = "width: " + this.labelWidth + 'px';
43457                     }
43458                     if(this.labelWidth < 13 && this.labelmd == 0){
43459                         this.labelmd = this.labelWidth;
43460                     }
43461                     if(this.labellg > 0){
43462                         label.cls += ' col-lg-' + this.labellg;
43463                         input.cls += ' col-lg-' + (12 - this.labellg);
43464                     }
43465                     if(this.labelmd > 0){
43466                         label.cls += ' col-md-' + this.labelmd;
43467                         container.cls += ' col-md-' + (12 - this.labelmd);
43468                     }
43469                     if(this.labelsm > 0){
43470                         label.cls += ' col-sm-' + this.labelsm;
43471                         container.cls += ' col-sm-' + (12 - this.labelsm);
43472                     }
43473                     if(this.labelxs > 0){
43474                         label.cls += ' col-xs-' + this.labelxs;
43475                         container.cls += ' col-xs-' + (12 - this.labelxs);
43476                     }
43477                 }
43478             }
43479             
43480             cfg.cn = [
43481                 label,
43482                 container
43483             ];
43484             
43485             var settings = this;
43486             
43487             ['xs','sm','md','lg'].map(function(size){
43488                 if (settings[size]) {
43489                     cfg.cls += ' col-' + size + '-' + settings[size];
43490                 }
43491             });
43492             
43493             this.store = new Roo.data.Store({
43494                 proxy : new Roo.data.MemoryProxy({}),
43495                 reader : new Roo.data.JsonReader({
43496                     fields : [
43497                         {
43498                             'name' : 'name',
43499                             'type' : 'string'
43500                         },
43501                         {
43502                             'name' : 'iso2',
43503                             'type' : 'string'
43504                         },
43505                         {
43506                             'name' : 'dialCode',
43507                             'type' : 'string'
43508                         },
43509                         {
43510                             'name' : 'priority',
43511                             'type' : 'string'
43512                         },
43513                         {
43514                             'name' : 'areaCodes',
43515                             'type' : 'string'
43516                         }
43517                     ]
43518                 })
43519             });
43520             
43521             if(!this.preferedCountries) {
43522                 this.preferedCountries = [
43523                     'hk',
43524                     'gb',
43525                     'us'
43526                 ];
43527             }
43528             
43529             var p = this.preferedCountries.reverse();
43530             
43531             if(p) {
43532                 for (var i = 0; i < p.length; i++) {
43533                     for (var j = 0; j < this.allCountries.length; j++) {
43534                         if(this.allCountries[j].iso2 == p[i]) {
43535                             var t = this.allCountries[j];
43536                             this.allCountries.splice(j,1);
43537                             this.allCountries.unshift(t);
43538                         }
43539                     } 
43540                 }
43541             }
43542             
43543             this.store.proxy.data = {
43544                 success: true,
43545                 data: this.allCountries
43546             };
43547             
43548             return cfg;
43549         },
43550         
43551         initEvents : function()
43552         {
43553             this.createList();
43554             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43555             
43556             this.indicator = this.indicatorEl();
43557             this.flag = this.flagEl();
43558             this.dialCodeHolder = this.dialCodeHolderEl();
43559             
43560             this.trigger = this.el.select('div.flag-box',true).first();
43561             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43562             
43563             var _this = this;
43564             
43565             (function(){
43566                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43567                 _this.list.setWidth(lw);
43568             }).defer(100);
43569             
43570             this.list.on('mouseover', this.onViewOver, this);
43571             this.list.on('mousemove', this.onViewMove, this);
43572             this.inputEl().on("keyup", this.onKeyUp, this);
43573             this.inputEl().on("keypress", this.onKeyPress, this);
43574             
43575             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43576
43577             this.view = new Roo.View(this.list, this.tpl, {
43578                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43579             });
43580             
43581             this.view.on('click', this.onViewClick, this);
43582             this.setValue(this.defaultDialCode);
43583         },
43584         
43585         onTriggerClick : function(e)
43586         {
43587             Roo.log('trigger click');
43588             if(this.disabled){
43589                 return;
43590             }
43591             
43592             if(this.isExpanded()){
43593                 this.collapse();
43594                 this.hasFocus = false;
43595             }else {
43596                 this.store.load({});
43597                 this.hasFocus = true;
43598                 this.expand();
43599             }
43600         },
43601         
43602         isExpanded : function()
43603         {
43604             return this.list.isVisible();
43605         },
43606         
43607         collapse : function()
43608         {
43609             if(!this.isExpanded()){
43610                 return;
43611             }
43612             this.list.hide();
43613             Roo.get(document).un('mousedown', this.collapseIf, this);
43614             Roo.get(document).un('mousewheel', this.collapseIf, this);
43615             this.fireEvent('collapse', this);
43616             this.validate();
43617         },
43618         
43619         expand : function()
43620         {
43621             Roo.log('expand');
43622
43623             if(this.isExpanded() || !this.hasFocus){
43624                 return;
43625             }
43626             
43627             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43628             this.list.setWidth(lw);
43629             
43630             this.list.show();
43631             this.restrictHeight();
43632             
43633             Roo.get(document).on('mousedown', this.collapseIf, this);
43634             Roo.get(document).on('mousewheel', this.collapseIf, this);
43635             
43636             this.fireEvent('expand', this);
43637         },
43638         
43639         restrictHeight : function()
43640         {
43641             this.list.alignTo(this.inputEl(), this.listAlign);
43642             this.list.alignTo(this.inputEl(), this.listAlign);
43643         },
43644         
43645         onViewOver : function(e, t)
43646         {
43647             if(this.inKeyMode){
43648                 return;
43649             }
43650             var item = this.view.findItemFromChild(t);
43651             
43652             if(item){
43653                 var index = this.view.indexOf(item);
43654                 this.select(index, false);
43655             }
43656         },
43657
43658         // private
43659         onViewClick : function(view, doFocus, el, e)
43660         {
43661             var index = this.view.getSelectedIndexes()[0];
43662             
43663             var r = this.store.getAt(index);
43664             
43665             if(r){
43666                 this.onSelect(r, index);
43667             }
43668             if(doFocus !== false && !this.blockFocus){
43669                 this.inputEl().focus();
43670             }
43671         },
43672         
43673         onViewMove : function(e, t)
43674         {
43675             this.inKeyMode = false;
43676         },
43677         
43678         select : function(index, scrollIntoView)
43679         {
43680             this.selectedIndex = index;
43681             this.view.select(index);
43682             if(scrollIntoView !== false){
43683                 var el = this.view.getNode(index);
43684                 if(el){
43685                     this.list.scrollChildIntoView(el, false);
43686                 }
43687             }
43688         },
43689         
43690         createList : function()
43691         {
43692             this.list = Roo.get(document.body).createChild({
43693                 tag: 'ul',
43694                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43695                 style: 'display:none'
43696             });
43697             
43698             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43699         },
43700         
43701         collapseIf : function(e)
43702         {
43703             var in_combo  = e.within(this.el);
43704             var in_list =  e.within(this.list);
43705             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43706             
43707             if (in_combo || in_list || is_list) {
43708                 return;
43709             }
43710             this.collapse();
43711         },
43712         
43713         onSelect : function(record, index)
43714         {
43715             if(this.fireEvent('beforeselect', this, record, index) !== false){
43716                 
43717                 this.setFlagClass(record.data.iso2);
43718                 this.setDialCode(record.data.dialCode);
43719                 this.hasFocus = false;
43720                 this.collapse();
43721                 this.fireEvent('select', this, record, index);
43722             }
43723         },
43724         
43725         flagEl : function()
43726         {
43727             var flag = this.el.select('div.flag',true).first();
43728             if(!flag){
43729                 return false;
43730             }
43731             return flag;
43732         },
43733         
43734         dialCodeHolderEl : function()
43735         {
43736             var d = this.el.select('input.dial-code-holder',true).first();
43737             if(!d){
43738                 return false;
43739             }
43740             return d;
43741         },
43742         
43743         setDialCode : function(v)
43744         {
43745             this.dialCodeHolder.dom.value = '+'+v;
43746         },
43747         
43748         setFlagClass : function(n)
43749         {
43750             this.flag.dom.className = 'flag '+n;
43751         },
43752         
43753         getValue : function()
43754         {
43755             var v = this.inputEl().getValue();
43756             if(this.dialCodeHolder) {
43757                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43758             }
43759             return v;
43760         },
43761         
43762         setValue : function(v)
43763         {
43764             var d = this.getDialCode(v);
43765             
43766             //invalid dial code
43767             if(v.length == 0 || !d || d.length == 0) {
43768                 if(this.rendered){
43769                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43770                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43771                 }
43772                 return;
43773             }
43774             
43775             //valid dial code
43776             this.setFlagClass(this.dialCodeMapping[d].iso2);
43777             this.setDialCode(d);
43778             this.inputEl().dom.value = v.replace('+'+d,'');
43779             this.hiddenEl().dom.value = this.getValue();
43780             
43781             this.validate();
43782         },
43783         
43784         getDialCode : function(v)
43785         {
43786             v = v ||  '';
43787             
43788             if (v.length == 0) {
43789                 return this.dialCodeHolder.dom.value;
43790             }
43791             
43792             var dialCode = "";
43793             if (v.charAt(0) != "+") {
43794                 return false;
43795             }
43796             var numericChars = "";
43797             for (var i = 1; i < v.length; i++) {
43798               var c = v.charAt(i);
43799               if (!isNaN(c)) {
43800                 numericChars += c;
43801                 if (this.dialCodeMapping[numericChars]) {
43802                   dialCode = v.substr(1, i);
43803                 }
43804                 if (numericChars.length == 4) {
43805                   break;
43806                 }
43807               }
43808             }
43809             return dialCode;
43810         },
43811         
43812         reset : function()
43813         {
43814             this.setValue(this.defaultDialCode);
43815             this.validate();
43816         },
43817         
43818         hiddenEl : function()
43819         {
43820             return this.el.select('input.hidden-tel-input',true).first();
43821         },
43822         
43823         // after setting val
43824         onKeyUp : function(e){
43825             this.setValue(this.getValue());
43826         },
43827         
43828         onKeyPress : function(e){
43829             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43830                 e.stopEvent();
43831             }
43832         }
43833         
43834 });
43835 /**
43836  * @class Roo.bootstrap.form.MoneyField
43837  * @extends Roo.bootstrap.form.ComboBox
43838  * Bootstrap MoneyField class
43839  * 
43840  * @constructor
43841  * Create a new MoneyField.
43842  * @param {Object} config Configuration options
43843  */
43844
43845 Roo.bootstrap.form.MoneyField = function(config) {
43846     
43847     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43848     
43849 };
43850
43851 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43852     
43853     /**
43854      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43855      */
43856     allowDecimals : true,
43857     /**
43858      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43859      */
43860     decimalSeparator : ".",
43861     /**
43862      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43863      */
43864     decimalPrecision : 0,
43865     /**
43866      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43867      */
43868     allowNegative : true,
43869     /**
43870      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43871      */
43872     allowZero: true,
43873     /**
43874      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43875      */
43876     minValue : Number.NEGATIVE_INFINITY,
43877     /**
43878      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43879      */
43880     maxValue : Number.MAX_VALUE,
43881     /**
43882      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43883      */
43884     minText : "The minimum value for this field is {0}",
43885     /**
43886      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43887      */
43888     maxText : "The maximum value for this field is {0}",
43889     /**
43890      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43891      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43892      */
43893     nanText : "{0} is not a valid number",
43894     /**
43895      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43896      */
43897     castInt : true,
43898     /**
43899      * @cfg {String} defaults currency of the MoneyField
43900      * value should be in lkey
43901      */
43902     defaultCurrency : false,
43903     /**
43904      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43905      */
43906     thousandsDelimiter : false,
43907     /**
43908      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43909      */
43910     max_length: false,
43911     
43912     inputlg : 9,
43913     inputmd : 9,
43914     inputsm : 9,
43915     inputxs : 6,
43916      /**
43917      * @cfg {Roo.data.Store} store  Store to lookup currency??
43918      */
43919     store : false,
43920     
43921     getAutoCreate : function()
43922     {
43923         var align = this.labelAlign || this.parentLabelAlign();
43924         
43925         var id = Roo.id();
43926
43927         var cfg = {
43928             cls: 'form-group',
43929             cn: []
43930         };
43931
43932         var input =  {
43933             tag: 'input',
43934             id : id,
43935             cls : 'form-control roo-money-amount-input',
43936             autocomplete: 'new-password'
43937         };
43938         
43939         var hiddenInput = {
43940             tag: 'input',
43941             type: 'hidden',
43942             id: Roo.id(),
43943             cls: 'hidden-number-input'
43944         };
43945         
43946         if(this.max_length) {
43947             input.maxlength = this.max_length; 
43948         }
43949         
43950         if (this.name) {
43951             hiddenInput.name = this.name;
43952         }
43953
43954         if (this.disabled) {
43955             input.disabled = true;
43956         }
43957
43958         var clg = 12 - this.inputlg;
43959         var cmd = 12 - this.inputmd;
43960         var csm = 12 - this.inputsm;
43961         var cxs = 12 - this.inputxs;
43962         
43963         var container = {
43964             tag : 'div',
43965             cls : 'row roo-money-field',
43966             cn : [
43967                 {
43968                     tag : 'div',
43969                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43970                     cn : [
43971                         {
43972                             tag : 'div',
43973                             cls: 'roo-select2-container input-group',
43974                             cn: [
43975                                 {
43976                                     tag : 'input',
43977                                     cls : 'form-control roo-money-currency-input',
43978                                     autocomplete: 'new-password',
43979                                     readOnly : 1,
43980                                     name : this.currencyName
43981                                 },
43982                                 {
43983                                     tag :'span',
43984                                     cls : 'input-group-addon',
43985                                     cn : [
43986                                         {
43987                                             tag: 'span',
43988                                             cls: 'caret'
43989                                         }
43990                                     ]
43991                                 }
43992                             ]
43993                         }
43994                     ]
43995                 },
43996                 {
43997                     tag : 'div',
43998                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43999                     cn : [
44000                         {
44001                             tag: 'div',
44002                             cls: this.hasFeedback ? 'has-feedback' : '',
44003                             cn: [
44004                                 input
44005                             ]
44006                         }
44007                     ]
44008                 }
44009             ]
44010             
44011         };
44012         
44013         if (this.fieldLabel.length) {
44014             var indicator = {
44015                 tag: 'i',
44016                 tooltip: 'This field is required'
44017             };
44018
44019             var label = {
44020                 tag: 'label',
44021                 'for':  id,
44022                 cls: 'control-label',
44023                 cn: []
44024             };
44025
44026             var label_text = {
44027                 tag: 'span',
44028                 html: this.fieldLabel
44029             };
44030
44031             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44032             label.cn = [
44033                 indicator,
44034                 label_text
44035             ];
44036
44037             if(this.indicatorpos == 'right') {
44038                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44039                 label.cn = [
44040                     label_text,
44041                     indicator
44042                 ];
44043             }
44044
44045             if(align == 'left') {
44046                 container = {
44047                     tag: 'div',
44048                     cn: [
44049                         container
44050                     ]
44051                 };
44052
44053                 if(this.labelWidth > 12){
44054                     label.style = "width: " + this.labelWidth + 'px';
44055                 }
44056                 if(this.labelWidth < 13 && this.labelmd == 0){
44057                     this.labelmd = this.labelWidth;
44058                 }
44059                 if(this.labellg > 0){
44060                     label.cls += ' col-lg-' + this.labellg;
44061                     input.cls += ' col-lg-' + (12 - this.labellg);
44062                 }
44063                 if(this.labelmd > 0){
44064                     label.cls += ' col-md-' + this.labelmd;
44065                     container.cls += ' col-md-' + (12 - this.labelmd);
44066                 }
44067                 if(this.labelsm > 0){
44068                     label.cls += ' col-sm-' + this.labelsm;
44069                     container.cls += ' col-sm-' + (12 - this.labelsm);
44070                 }
44071                 if(this.labelxs > 0){
44072                     label.cls += ' col-xs-' + this.labelxs;
44073                     container.cls += ' col-xs-' + (12 - this.labelxs);
44074                 }
44075             }
44076         }
44077
44078         cfg.cn = [
44079             label,
44080             container,
44081             hiddenInput
44082         ];
44083         
44084         var settings = this;
44085
44086         ['xs','sm','md','lg'].map(function(size){
44087             if (settings[size]) {
44088                 cfg.cls += ' col-' + size + '-' + settings[size];
44089             }
44090         });
44091         
44092         return cfg;
44093     },
44094     
44095     initEvents : function()
44096     {
44097         this.indicator = this.indicatorEl();
44098         
44099         this.initCurrencyEvent();
44100         
44101         this.initNumberEvent();
44102     },
44103     
44104     initCurrencyEvent : function()
44105     {
44106         if (!this.store) {
44107             throw "can not find store for combo";
44108         }
44109         
44110         this.store = Roo.factory(this.store, Roo.data);
44111         this.store.parent = this;
44112         
44113         this.createList();
44114         
44115         this.triggerEl = this.el.select('.input-group-addon', true).first();
44116         
44117         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44118         
44119         var _this = this;
44120         
44121         (function(){
44122             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44123             _this.list.setWidth(lw);
44124         }).defer(100);
44125         
44126         this.list.on('mouseover', this.onViewOver, this);
44127         this.list.on('mousemove', this.onViewMove, this);
44128         this.list.on('scroll', this.onViewScroll, this);
44129         
44130         if(!this.tpl){
44131             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44132         }
44133         
44134         this.view = new Roo.View(this.list, this.tpl, {
44135             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44136         });
44137         
44138         this.view.on('click', this.onViewClick, this);
44139         
44140         this.store.on('beforeload', this.onBeforeLoad, this);
44141         this.store.on('load', this.onLoad, this);
44142         this.store.on('loadexception', this.onLoadException, this);
44143         
44144         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44145             "up" : function(e){
44146                 this.inKeyMode = true;
44147                 this.selectPrev();
44148             },
44149
44150             "down" : function(e){
44151                 if(!this.isExpanded()){
44152                     this.onTriggerClick();
44153                 }else{
44154                     this.inKeyMode = true;
44155                     this.selectNext();
44156                 }
44157             },
44158
44159             "enter" : function(e){
44160                 this.collapse();
44161                 
44162                 if(this.fireEvent("specialkey", this, e)){
44163                     this.onViewClick(false);
44164                 }
44165                 
44166                 return true;
44167             },
44168
44169             "esc" : function(e){
44170                 this.collapse();
44171             },
44172
44173             "tab" : function(e){
44174                 this.collapse();
44175                 
44176                 if(this.fireEvent("specialkey", this, e)){
44177                     this.onViewClick(false);
44178                 }
44179                 
44180                 return true;
44181             },
44182
44183             scope : this,
44184
44185             doRelay : function(foo, bar, hname){
44186                 if(hname == 'down' || this.scope.isExpanded()){
44187                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44188                 }
44189                 return true;
44190             },
44191
44192             forceKeyDown: true
44193         });
44194         
44195         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44196         
44197     },
44198     
44199     initNumberEvent : function(e)
44200     {
44201         this.inputEl().on("keydown" , this.fireKey,  this);
44202         this.inputEl().on("focus", this.onFocus,  this);
44203         this.inputEl().on("blur", this.onBlur,  this);
44204         
44205         this.inputEl().relayEvent('keyup', this);
44206         
44207         if(this.indicator){
44208             this.indicator.addClass('invisible');
44209         }
44210  
44211         this.originalValue = this.getValue();
44212         
44213         if(this.validationEvent == 'keyup'){
44214             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44215             this.inputEl().on('keyup', this.filterValidation, this);
44216         }
44217         else if(this.validationEvent !== false){
44218             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44219         }
44220         
44221         if(this.selectOnFocus){
44222             this.on("focus", this.preFocus, this);
44223             
44224         }
44225         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44226             this.inputEl().on("keypress", this.filterKeys, this);
44227         } else {
44228             this.inputEl().relayEvent('keypress', this);
44229         }
44230         
44231         var allowed = "0123456789";
44232         
44233         if(this.allowDecimals){
44234             allowed += this.decimalSeparator;
44235         }
44236         
44237         if(this.allowNegative){
44238             allowed += "-";
44239         }
44240         
44241         if(this.thousandsDelimiter) {
44242             allowed += ",";
44243         }
44244         
44245         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44246         
44247         var keyPress = function(e){
44248             
44249             var k = e.getKey();
44250             
44251             var c = e.getCharCode();
44252             
44253             if(
44254                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44255                     allowed.indexOf(String.fromCharCode(c)) === -1
44256             ){
44257                 e.stopEvent();
44258                 return;
44259             }
44260             
44261             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44262                 return;
44263             }
44264             
44265             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44266                 e.stopEvent();
44267             }
44268         };
44269         
44270         this.inputEl().on("keypress", keyPress, this);
44271         
44272     },
44273     
44274     onTriggerClick : function(e)
44275     {   
44276         if(this.disabled){
44277             return;
44278         }
44279         
44280         this.page = 0;
44281         this.loadNext = false;
44282         
44283         if(this.isExpanded()){
44284             this.collapse();
44285             return;
44286         }
44287         
44288         this.hasFocus = true;
44289         
44290         if(this.triggerAction == 'all') {
44291             this.doQuery(this.allQuery, true);
44292             return;
44293         }
44294         
44295         this.doQuery(this.getRawValue());
44296     },
44297     
44298     getCurrency : function()
44299     {   
44300         var v = this.currencyEl().getValue();
44301         
44302         return v;
44303     },
44304     
44305     restrictHeight : function()
44306     {
44307         this.list.alignTo(this.currencyEl(), this.listAlign);
44308         this.list.alignTo(this.currencyEl(), this.listAlign);
44309     },
44310     
44311     onViewClick : function(view, doFocus, el, e)
44312     {
44313         var index = this.view.getSelectedIndexes()[0];
44314         
44315         var r = this.store.getAt(index);
44316         
44317         if(r){
44318             this.onSelect(r, index);
44319         }
44320     },
44321     
44322     onSelect : function(record, index){
44323         
44324         if(this.fireEvent('beforeselect', this, record, index) !== false){
44325         
44326             this.setFromCurrencyData(index > -1 ? record.data : false);
44327             
44328             this.collapse();
44329             
44330             this.fireEvent('select', this, record, index);
44331         }
44332     },
44333     
44334     setFromCurrencyData : function(o)
44335     {
44336         var currency = '';
44337         
44338         this.lastCurrency = o;
44339         
44340         if (this.currencyField) {
44341             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44342         } else {
44343             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44344         }
44345         
44346         this.lastSelectionText = currency;
44347         
44348         //setting default currency
44349         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44350             this.setCurrency(this.defaultCurrency);
44351             return;
44352         }
44353         
44354         this.setCurrency(currency);
44355     },
44356     
44357     setFromData : function(o)
44358     {
44359         var c = {};
44360         
44361         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44362         
44363         this.setFromCurrencyData(c);
44364         
44365         var value = '';
44366         
44367         if (this.name) {
44368             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44369         } else {
44370             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44371         }
44372         
44373         this.setValue(value);
44374         
44375     },
44376     
44377     setCurrency : function(v)
44378     {   
44379         this.currencyValue = v;
44380         
44381         if(this.rendered){
44382             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44383             this.validate();
44384         }
44385     },
44386     
44387     setValue : function(v)
44388     {
44389         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44390         
44391         this.value = v;
44392         
44393         if(this.rendered){
44394             
44395             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44396             
44397             this.inputEl().dom.value = (v == '') ? '' :
44398                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44399             
44400             if(!this.allowZero && v === '0') {
44401                 this.hiddenEl().dom.value = '';
44402                 this.inputEl().dom.value = '';
44403             }
44404             
44405             this.validate();
44406         }
44407     },
44408     
44409     getRawValue : function()
44410     {
44411         var v = this.inputEl().getValue();
44412         
44413         return v;
44414     },
44415     
44416     getValue : function()
44417     {
44418         return this.fixPrecision(this.parseValue(this.getRawValue()));
44419     },
44420     
44421     parseValue : function(value)
44422     {
44423         if(this.thousandsDelimiter) {
44424             value += "";
44425             r = new RegExp(",", "g");
44426             value = value.replace(r, "");
44427         }
44428         
44429         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44430         return isNaN(value) ? '' : value;
44431         
44432     },
44433     
44434     fixPrecision : function(value)
44435     {
44436         if(this.thousandsDelimiter) {
44437             value += "";
44438             r = new RegExp(",", "g");
44439             value = value.replace(r, "");
44440         }
44441         
44442         var nan = isNaN(value);
44443         
44444         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44445             return nan ? '' : value;
44446         }
44447         return parseFloat(value).toFixed(this.decimalPrecision);
44448     },
44449     
44450     decimalPrecisionFcn : function(v)
44451     {
44452         return Math.floor(v);
44453     },
44454     
44455     validateValue : function(value)
44456     {
44457         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44458             return false;
44459         }
44460         
44461         var num = this.parseValue(value);
44462         
44463         if(isNaN(num)){
44464             this.markInvalid(String.format(this.nanText, value));
44465             return false;
44466         }
44467         
44468         if(num < this.minValue){
44469             this.markInvalid(String.format(this.minText, this.minValue));
44470             return false;
44471         }
44472         
44473         if(num > this.maxValue){
44474             this.markInvalid(String.format(this.maxText, this.maxValue));
44475             return false;
44476         }
44477         
44478         return true;
44479     },
44480     
44481     validate : function()
44482     {
44483         if(this.disabled || this.allowBlank){
44484             this.markValid();
44485             return true;
44486         }
44487         
44488         var currency = this.getCurrency();
44489         
44490         if(this.validateValue(this.getRawValue()) && currency.length){
44491             this.markValid();
44492             return true;
44493         }
44494         
44495         this.markInvalid();
44496         return false;
44497     },
44498     
44499     getName: function()
44500     {
44501         return this.name;
44502     },
44503     
44504     beforeBlur : function()
44505     {
44506         if(!this.castInt){
44507             return;
44508         }
44509         
44510         var v = this.parseValue(this.getRawValue());
44511         
44512         if(v || v == 0){
44513             this.setValue(v);
44514         }
44515     },
44516     
44517     onBlur : function()
44518     {
44519         this.beforeBlur();
44520         
44521         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44522             //this.el.removeClass(this.focusClass);
44523         }
44524         
44525         this.hasFocus = false;
44526         
44527         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44528             this.validate();
44529         }
44530         
44531         var v = this.getValue();
44532         
44533         if(String(v) !== String(this.startValue)){
44534             this.fireEvent('change', this, v, this.startValue);
44535         }
44536         
44537         this.fireEvent("blur", this);
44538     },
44539     
44540     inputEl : function()
44541     {
44542         return this.el.select('.roo-money-amount-input', true).first();
44543     },
44544     
44545     currencyEl : function()
44546     {
44547         return this.el.select('.roo-money-currency-input', true).first();
44548     },
44549     
44550     hiddenEl : function()
44551     {
44552         return this.el.select('input.hidden-number-input',true).first();
44553     }
44554     
44555 });/**
44556  * @class Roo.bootstrap.BezierSignature
44557  * @extends Roo.bootstrap.Component
44558  * Bootstrap BezierSignature class
44559  * This script refer to:
44560  *    Title: Signature Pad
44561  *    Author: szimek
44562  *    Availability: https://github.com/szimek/signature_pad
44563  *
44564  * @constructor
44565  * Create a new BezierSignature
44566  * @param {Object} config The config object
44567  */
44568
44569 Roo.bootstrap.BezierSignature = function(config){
44570     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44571     this.addEvents({
44572         "resize" : true
44573     });
44574 };
44575
44576 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44577 {
44578      
44579     curve_data: [],
44580     
44581     is_empty: true,
44582     
44583     mouse_btn_down: true,
44584     
44585     /**
44586      * @cfg {int} canvas height
44587      */
44588     canvas_height: '200px',
44589     
44590     /**
44591      * @cfg {float|function} Radius of a single dot.
44592      */ 
44593     dot_size: false,
44594     
44595     /**
44596      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44597      */
44598     min_width: 0.5,
44599     
44600     /**
44601      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44602      */
44603     max_width: 2.5,
44604     
44605     /**
44606      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44607      */
44608     throttle: 16,
44609     
44610     /**
44611      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44612      */
44613     min_distance: 5,
44614     
44615     /**
44616      * @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.
44617      */
44618     bg_color: 'rgba(0, 0, 0, 0)',
44619     
44620     /**
44621      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44622      */
44623     dot_color: 'black',
44624     
44625     /**
44626      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44627      */ 
44628     velocity_filter_weight: 0.7,
44629     
44630     /**
44631      * @cfg {function} Callback when stroke begin. 
44632      */
44633     onBegin: false,
44634     
44635     /**
44636      * @cfg {function} Callback when stroke end.
44637      */
44638     onEnd: false,
44639     
44640     getAutoCreate : function()
44641     {
44642         var cls = 'roo-signature column';
44643         
44644         if(this.cls){
44645             cls += ' ' + this.cls;
44646         }
44647         
44648         var col_sizes = [
44649             'lg',
44650             'md',
44651             'sm',
44652             'xs'
44653         ];
44654         
44655         for(var i = 0; i < col_sizes.length; i++) {
44656             if(this[col_sizes[i]]) {
44657                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44658             }
44659         }
44660         
44661         var cfg = {
44662             tag: 'div',
44663             cls: cls,
44664             cn: [
44665                 {
44666                     tag: 'div',
44667                     cls: 'roo-signature-body',
44668                     cn: [
44669                         {
44670                             tag: 'canvas',
44671                             cls: 'roo-signature-body-canvas',
44672                             height: this.canvas_height,
44673                             width: this.canvas_width
44674                         }
44675                     ]
44676                 },
44677                 {
44678                     tag: 'input',
44679                     type: 'file',
44680                     style: 'display: none'
44681                 }
44682             ]
44683         };
44684         
44685         return cfg;
44686     },
44687     
44688     initEvents: function() 
44689     {
44690         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44691         
44692         var canvas = this.canvasEl();
44693         
44694         // mouse && touch event swapping...
44695         canvas.dom.style.touchAction = 'none';
44696         canvas.dom.style.msTouchAction = 'none';
44697         
44698         this.mouse_btn_down = false;
44699         canvas.on('mousedown', this._handleMouseDown, this);
44700         canvas.on('mousemove', this._handleMouseMove, this);
44701         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44702         
44703         if (window.PointerEvent) {
44704             canvas.on('pointerdown', this._handleMouseDown, this);
44705             canvas.on('pointermove', this._handleMouseMove, this);
44706             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44707         }
44708         
44709         if ('ontouchstart' in window) {
44710             canvas.on('touchstart', this._handleTouchStart, this);
44711             canvas.on('touchmove', this._handleTouchMove, this);
44712             canvas.on('touchend', this._handleTouchEnd, this);
44713         }
44714         
44715         Roo.EventManager.onWindowResize(this.resize, this, true);
44716         
44717         // file input event
44718         this.fileEl().on('change', this.uploadImage, this);
44719         
44720         this.clear();
44721         
44722         this.resize();
44723     },
44724     
44725     resize: function(){
44726         
44727         var canvas = this.canvasEl().dom;
44728         var ctx = this.canvasElCtx();
44729         var img_data = false;
44730         
44731         if(canvas.width > 0) {
44732             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44733         }
44734         // setting canvas width will clean img data
44735         canvas.width = 0;
44736         
44737         var style = window.getComputedStyle ? 
44738             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44739             
44740         var padding_left = parseInt(style.paddingLeft) || 0;
44741         var padding_right = parseInt(style.paddingRight) || 0;
44742         
44743         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44744         
44745         if(img_data) {
44746             ctx.putImageData(img_data, 0, 0);
44747         }
44748     },
44749     
44750     _handleMouseDown: function(e)
44751     {
44752         if (e.browserEvent.which === 1) {
44753             this.mouse_btn_down = true;
44754             this.strokeBegin(e);
44755         }
44756     },
44757     
44758     _handleMouseMove: function (e)
44759     {
44760         if (this.mouse_btn_down) {
44761             this.strokeMoveUpdate(e);
44762         }
44763     },
44764     
44765     _handleMouseUp: function (e)
44766     {
44767         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44768             this.mouse_btn_down = false;
44769             this.strokeEnd(e);
44770         }
44771     },
44772     
44773     _handleTouchStart: function (e) {
44774         
44775         e.preventDefault();
44776         if (e.browserEvent.targetTouches.length === 1) {
44777             // var touch = e.browserEvent.changedTouches[0];
44778             // this.strokeBegin(touch);
44779             
44780              this.strokeBegin(e); // assume e catching the correct xy...
44781         }
44782     },
44783     
44784     _handleTouchMove: function (e) {
44785         e.preventDefault();
44786         // var touch = event.targetTouches[0];
44787         // _this._strokeMoveUpdate(touch);
44788         this.strokeMoveUpdate(e);
44789     },
44790     
44791     _handleTouchEnd: function (e) {
44792         var wasCanvasTouched = e.target === this.canvasEl().dom;
44793         if (wasCanvasTouched) {
44794             e.preventDefault();
44795             // var touch = event.changedTouches[0];
44796             // _this._strokeEnd(touch);
44797             this.strokeEnd(e);
44798         }
44799     },
44800     
44801     reset: function () {
44802         this._lastPoints = [];
44803         this._lastVelocity = 0;
44804         this._lastWidth = (this.min_width + this.max_width) / 2;
44805         this.canvasElCtx().fillStyle = this.dot_color;
44806     },
44807     
44808     strokeMoveUpdate: function(e)
44809     {
44810         this.strokeUpdate(e);
44811         
44812         if (this.throttle) {
44813             this.throttleStroke(this.strokeUpdate, this.throttle);
44814         }
44815         else {
44816             this.strokeUpdate(e);
44817         }
44818     },
44819     
44820     strokeBegin: function(e)
44821     {
44822         var newPointGroup = {
44823             color: this.dot_color,
44824             points: []
44825         };
44826         
44827         if (typeof this.onBegin === 'function') {
44828             this.onBegin(e);
44829         }
44830         
44831         this.curve_data.push(newPointGroup);
44832         this.reset();
44833         this.strokeUpdate(e);
44834     },
44835     
44836     strokeUpdate: function(e)
44837     {
44838         var rect = this.canvasEl().dom.getBoundingClientRect();
44839         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44840         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44841         var lastPoints = lastPointGroup.points;
44842         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44843         var isLastPointTooClose = lastPoint
44844             ? point.distanceTo(lastPoint) <= this.min_distance
44845             : false;
44846         var color = lastPointGroup.color;
44847         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44848             var curve = this.addPoint(point);
44849             if (!lastPoint) {
44850                 this.drawDot({color: color, point: point});
44851             }
44852             else if (curve) {
44853                 this.drawCurve({color: color, curve: curve});
44854             }
44855             lastPoints.push({
44856                 time: point.time,
44857                 x: point.x,
44858                 y: point.y
44859             });
44860         }
44861     },
44862     
44863     strokeEnd: function(e)
44864     {
44865         this.strokeUpdate(e);
44866         if (typeof this.onEnd === 'function') {
44867             this.onEnd(e);
44868         }
44869     },
44870     
44871     addPoint:  function (point) {
44872         var _lastPoints = this._lastPoints;
44873         _lastPoints.push(point);
44874         if (_lastPoints.length > 2) {
44875             if (_lastPoints.length === 3) {
44876                 _lastPoints.unshift(_lastPoints[0]);
44877             }
44878             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44879             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44880             _lastPoints.shift();
44881             return curve;
44882         }
44883         return null;
44884     },
44885     
44886     calculateCurveWidths: function (startPoint, endPoint) {
44887         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44888             (1 - this.velocity_filter_weight) * this._lastVelocity;
44889
44890         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44891         var widths = {
44892             end: newWidth,
44893             start: this._lastWidth
44894         };
44895         
44896         this._lastVelocity = velocity;
44897         this._lastWidth = newWidth;
44898         return widths;
44899     },
44900     
44901     drawDot: function (_a) {
44902         var color = _a.color, point = _a.point;
44903         var ctx = this.canvasElCtx();
44904         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44905         ctx.beginPath();
44906         this.drawCurveSegment(point.x, point.y, width);
44907         ctx.closePath();
44908         ctx.fillStyle = color;
44909         ctx.fill();
44910     },
44911     
44912     drawCurve: function (_a) {
44913         var color = _a.color, curve = _a.curve;
44914         var ctx = this.canvasElCtx();
44915         var widthDelta = curve.endWidth - curve.startWidth;
44916         var drawSteps = Math.floor(curve.length()) * 2;
44917         ctx.beginPath();
44918         ctx.fillStyle = color;
44919         for (var i = 0; i < drawSteps; i += 1) {
44920         var t = i / drawSteps;
44921         var tt = t * t;
44922         var ttt = tt * t;
44923         var u = 1 - t;
44924         var uu = u * u;
44925         var uuu = uu * u;
44926         var x = uuu * curve.startPoint.x;
44927         x += 3 * uu * t * curve.control1.x;
44928         x += 3 * u * tt * curve.control2.x;
44929         x += ttt * curve.endPoint.x;
44930         var y = uuu * curve.startPoint.y;
44931         y += 3 * uu * t * curve.control1.y;
44932         y += 3 * u * tt * curve.control2.y;
44933         y += ttt * curve.endPoint.y;
44934         var width = curve.startWidth + ttt * widthDelta;
44935         this.drawCurveSegment(x, y, width);
44936         }
44937         ctx.closePath();
44938         ctx.fill();
44939     },
44940     
44941     drawCurveSegment: function (x, y, width) {
44942         var ctx = this.canvasElCtx();
44943         ctx.moveTo(x, y);
44944         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44945         this.is_empty = false;
44946     },
44947     
44948     clear: function()
44949     {
44950         var ctx = this.canvasElCtx();
44951         var canvas = this.canvasEl().dom;
44952         ctx.fillStyle = this.bg_color;
44953         ctx.clearRect(0, 0, canvas.width, canvas.height);
44954         ctx.fillRect(0, 0, canvas.width, canvas.height);
44955         this.curve_data = [];
44956         this.reset();
44957         this.is_empty = true;
44958     },
44959     
44960     fileEl: function()
44961     {
44962         return  this.el.select('input',true).first();
44963     },
44964     
44965     canvasEl: function()
44966     {
44967         return this.el.select('canvas',true).first();
44968     },
44969     
44970     canvasElCtx: function()
44971     {
44972         return this.el.select('canvas',true).first().dom.getContext('2d');
44973     },
44974     
44975     getImage: function(type)
44976     {
44977         if(this.is_empty) {
44978             return false;
44979         }
44980         
44981         // encryption ?
44982         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44983     },
44984     
44985     drawFromImage: function(img_src)
44986     {
44987         var img = new Image();
44988         
44989         img.onload = function(){
44990             this.canvasElCtx().drawImage(img, 0, 0);
44991         }.bind(this);
44992         
44993         img.src = img_src;
44994         
44995         this.is_empty = false;
44996     },
44997     
44998     selectImage: function()
44999     {
45000         this.fileEl().dom.click();
45001     },
45002     
45003     uploadImage: function(e)
45004     {
45005         var reader = new FileReader();
45006         
45007         reader.onload = function(e){
45008             var img = new Image();
45009             img.onload = function(){
45010                 this.reset();
45011                 this.canvasElCtx().drawImage(img, 0, 0);
45012             }.bind(this);
45013             img.src = e.target.result;
45014         }.bind(this);
45015         
45016         reader.readAsDataURL(e.target.files[0]);
45017     },
45018     
45019     // Bezier Point Constructor
45020     Point: (function () {
45021         function Point(x, y, time) {
45022             this.x = x;
45023             this.y = y;
45024             this.time = time || Date.now();
45025         }
45026         Point.prototype.distanceTo = function (start) {
45027             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45028         };
45029         Point.prototype.equals = function (other) {
45030             return this.x === other.x && this.y === other.y && this.time === other.time;
45031         };
45032         Point.prototype.velocityFrom = function (start) {
45033             return this.time !== start.time
45034             ? this.distanceTo(start) / (this.time - start.time)
45035             : 0;
45036         };
45037         return Point;
45038     }()),
45039     
45040     
45041     // Bezier Constructor
45042     Bezier: (function () {
45043         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45044             this.startPoint = startPoint;
45045             this.control2 = control2;
45046             this.control1 = control1;
45047             this.endPoint = endPoint;
45048             this.startWidth = startWidth;
45049             this.endWidth = endWidth;
45050         }
45051         Bezier.fromPoints = function (points, widths, scope) {
45052             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45053             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45054             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45055         };
45056         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45057             var dx1 = s1.x - s2.x;
45058             var dy1 = s1.y - s2.y;
45059             var dx2 = s2.x - s3.x;
45060             var dy2 = s2.y - s3.y;
45061             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45062             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45063             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45064             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45065             var dxm = m1.x - m2.x;
45066             var dym = m1.y - m2.y;
45067             var k = l2 / (l1 + l2);
45068             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45069             var tx = s2.x - cm.x;
45070             var ty = s2.y - cm.y;
45071             return {
45072                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45073                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45074             };
45075         };
45076         Bezier.prototype.length = function () {
45077             var steps = 10;
45078             var length = 0;
45079             var px;
45080             var py;
45081             for (var i = 0; i <= steps; i += 1) {
45082                 var t = i / steps;
45083                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45084                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45085                 if (i > 0) {
45086                     var xdiff = cx - px;
45087                     var ydiff = cy - py;
45088                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45089                 }
45090                 px = cx;
45091                 py = cy;
45092             }
45093             return length;
45094         };
45095         Bezier.prototype.point = function (t, start, c1, c2, end) {
45096             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45097             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45098             + (3.0 * c2 * (1.0 - t) * t * t)
45099             + (end * t * t * t);
45100         };
45101         return Bezier;
45102     }()),
45103     
45104     throttleStroke: function(fn, wait) {
45105       if (wait === void 0) { wait = 250; }
45106       var previous = 0;
45107       var timeout = null;
45108       var result;
45109       var storedContext;
45110       var storedArgs;
45111       var later = function () {
45112           previous = Date.now();
45113           timeout = null;
45114           result = fn.apply(storedContext, storedArgs);
45115           if (!timeout) {
45116               storedContext = null;
45117               storedArgs = [];
45118           }
45119       };
45120       return function wrapper() {
45121           var args = [];
45122           for (var _i = 0; _i < arguments.length; _i++) {
45123               args[_i] = arguments[_i];
45124           }
45125           var now = Date.now();
45126           var remaining = wait - (now - previous);
45127           storedContext = this;
45128           storedArgs = args;
45129           if (remaining <= 0 || remaining > wait) {
45130               if (timeout) {
45131                   clearTimeout(timeout);
45132                   timeout = null;
45133               }
45134               previous = now;
45135               result = fn.apply(storedContext, storedArgs);
45136               if (!timeout) {
45137                   storedContext = null;
45138                   storedArgs = [];
45139               }
45140           }
45141           else if (!timeout) {
45142               timeout = window.setTimeout(later, remaining);
45143           }
45144           return result;
45145       };
45146   }
45147   
45148 });
45149
45150  
45151
45152  // old names for form elements
45153 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form.Form;
45154 Roo.bootstrap.Input         =   Roo.bootstrap.form.Form.Input;
45155 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.Form.TextArea;
45156 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.Form.TriggerField;
45157 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.Form.ComboBox;
45158 Roo.bootstrap.DateField     =   Roo.bootstrap.form.Form.DateField;
45159 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.Form.TimeField;
45160 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.Form.MonthField;
45161 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.Form.CheckBox;
45162 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Form.Radio;
45163 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.Form.RadioSet;
45164 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.Form.SecurePass;
45165 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.Form.FieldLabel;
45166 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.Form.DateSplitField;
45167 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.Form.NumberField;
45168 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.Form.PhoneInput;
45169 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.Form.PhoneInputData;
45170 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.Form.MoneyField;
45171 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.Form.HtmlEditor;
45172 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.Form.HtmlEditorToolbarStandard;
45173 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45174 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45175 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45176 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45177 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45178 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45179
45180 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45181 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45182
45183 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45184 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45185
45186 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45187 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45188 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45189 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45190