handle exception on store load if paging toolbar is used
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938         var im = {
2939             tag: 'input',
2940             type : 'file',
2941             cls : 'd-none  roo-card-upload-selector' 
2942           
2943         };
2944         if (this.multiple) {
2945             im.multiple = 'multiple';
2946         }
2947         
2948         return  {
2949             cls :'div' ,
2950             cn : [
2951                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2952                 im
2953
2954             ]
2955         };
2956            
2957          
2958     },
2959      
2960    
2961     initEvents : function()
2962     {
2963         
2964         Roo.bootstrap.Button.prototype.initEvents.call(this);
2965         
2966         
2967         
2968         
2969         
2970         this.urlAPI = (window.createObjectURL && window) || 
2971                                 (window.URL && URL.revokeObjectURL && URL) || 
2972                                 (window.webkitURL && webkitURL);
2973                         
2974          
2975          
2976          
2977         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2978         
2979         this.selectorEl.on('change', this.onFileSelected, this);
2980          
2981          
2982        
2983     },
2984     
2985    
2986     onClick : function(e)
2987     {
2988         e.preventDefault();
2989         
2990         if ( this.fireEvent('beforeselect', this) === false) {
2991             return;
2992         }
2993          
2994         this.selectorEl.dom.click();
2995          
2996     },
2997     
2998     onFileSelected : function(e)
2999     {
3000         e.preventDefault();
3001         
3002         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003             return;
3004         }
3005         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006         this.selectorEl.dom.value  = '';// hopefully reset..
3007         
3008         this.fireEvent('uploaded', this,  files );
3009         
3010     },
3011     
3012        
3013    
3014     
3015     /**
3016      * addCard - add an Attachment to the uploader
3017      * @param data - the data about the image to upload
3018      *
3019      * {
3020           id : 123
3021           title : "Title of file",
3022           is_uploaded : false,
3023           src : "http://.....",
3024           srcfile : { the File upload object },
3025           mimetype : file.type,
3026           preview : false,
3027           is_deleted : 0
3028           .. any other data...
3029         }
3030      *
3031      * 
3032     */
3033      
3034     reset: function()
3035     {
3036          
3037          this.selectorEl
3038     } 
3039     
3040     
3041     
3042     
3043 });
3044  /*
3045  * - LGPL
3046  *
3047  * image
3048  * 
3049  */
3050
3051
3052 /**
3053  * @class Roo.bootstrap.Img
3054  * @extends Roo.bootstrap.Component
3055  * Bootstrap Img class
3056  * @cfg {Boolean} imgResponsive false | true
3057  * @cfg {String} border rounded | circle | thumbnail
3058  * @cfg {String} src image source
3059  * @cfg {String} alt image alternative text
3060  * @cfg {String} href a tag href
3061  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062  * @cfg {String} xsUrl xs image source
3063  * @cfg {String} smUrl sm image source
3064  * @cfg {String} mdUrl md image source
3065  * @cfg {String} lgUrl lg image source
3066  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067  * 
3068  * @constructor
3069  * Create a new Input
3070  * @param {Object} config The config object
3071  */
3072
3073 Roo.bootstrap.Img = function(config){
3074     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3075     
3076     this.addEvents({
3077         // img events
3078         /**
3079          * @event click
3080          * The img click event for the img.
3081          * @param {Roo.EventObject} e
3082          */
3083         "click" : true,
3084         /**
3085          * @event load
3086          * The when any image loads
3087          * @param {Roo.EventObject} e
3088          */
3089         "load" : true
3090     });
3091 };
3092
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3094     
3095     imgResponsive: true,
3096     border: '',
3097     src: 'about:blank',
3098     href: false,
3099     target: false,
3100     xsUrl: '',
3101     smUrl: '',
3102     mdUrl: '',
3103     lgUrl: '',
3104     backgroundContain : false,
3105
3106     getAutoCreate : function()
3107     {   
3108         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109             return this.createSingleImg();
3110         }
3111         
3112         var cfg = {
3113             tag: 'div',
3114             cls: 'roo-image-responsive-group',
3115             cn: []
3116         };
3117         var _this = this;
3118         
3119         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3120             
3121             if(!_this[size + 'Url']){
3122                 return;
3123             }
3124             
3125             var img = {
3126                 tag: 'img',
3127                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128                 html: _this.html || cfg.html,
3129                 src: _this[size + 'Url']
3130             };
3131             
3132             img.cls += ' roo-image-responsive-' + size;
3133             
3134             var s = ['xs', 'sm', 'md', 'lg'];
3135             
3136             s.splice(s.indexOf(size), 1);
3137             
3138             Roo.each(s, function(ss){
3139                 img.cls += ' hidden-' + ss;
3140             });
3141             
3142             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143                 cfg.cls += ' img-' + _this.border;
3144             }
3145             
3146             if(_this.alt){
3147                 cfg.alt = _this.alt;
3148             }
3149             
3150             if(_this.href){
3151                 var a = {
3152                     tag: 'a',
3153                     href: _this.href,
3154                     cn: [
3155                         img
3156                     ]
3157                 };
3158
3159                 if(this.target){
3160                     a.target = _this.target;
3161                 }
3162             }
3163             
3164             cfg.cn.push((_this.href) ? a : img);
3165             
3166         });
3167         
3168         return cfg;
3169     },
3170     
3171     createSingleImg : function()
3172     {
3173         var cfg = {
3174             tag: 'img',
3175             cls: (this.imgResponsive) ? 'img-responsive' : '',
3176             html : null,
3177             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3178         };
3179         
3180         if (this.backgroundContain) {
3181             cfg.cls += ' background-contain';
3182         }
3183         
3184         cfg.html = this.html || cfg.html;
3185         
3186         if (this.backgroundContain) {
3187             cfg.style="background-image: url(" + this.src + ')';
3188         } else {
3189             cfg.src = this.src || cfg.src;
3190         }
3191         
3192         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193             cfg.cls += ' img-' + this.border;
3194         }
3195         
3196         if(this.alt){
3197             cfg.alt = this.alt;
3198         }
3199         
3200         if(this.href){
3201             var a = {
3202                 tag: 'a',
3203                 href: this.href,
3204                 cn: [
3205                     cfg
3206                 ]
3207             };
3208             
3209             if(this.target){
3210                 a.target = this.target;
3211             }
3212             
3213         }
3214         
3215         return (this.href) ? a : cfg;
3216     },
3217     
3218     initEvents: function() 
3219     {
3220         if(!this.href){
3221             this.el.on('click', this.onClick, this);
3222         }
3223         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224             this.el.on('load', this.onImageLoad, this);
3225         } else {
3226             // not sure if this works.. not tested
3227             this.el.select('img', true).on('load', this.onImageLoad, this);
3228         }
3229         
3230     },
3231     
3232     onClick : function(e)
3233     {
3234         Roo.log('img onclick');
3235         this.fireEvent('click', this, e);
3236     },
3237     onImageLoad: function(e)
3238     {
3239         Roo.log('img load');
3240         this.fireEvent('load', this, e);
3241     },
3242     
3243     /**
3244      * Sets the url of the image - used to update it
3245      * @param {String} url the url of the image
3246      */
3247     
3248     setSrc : function(url)
3249     {
3250         this.src =  url;
3251         
3252         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253             if (this.backgroundContain) {
3254                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3255             } else {
3256                 this.el.dom.src =  url;
3257             }
3258             return;
3259         }
3260         
3261         this.el.select('img', true).first().dom.src =  url;
3262     }
3263     
3264     
3265    
3266 });
3267
3268  /*
3269  * - LGPL
3270  *
3271  * image
3272  * 
3273  */
3274
3275
3276 /**
3277  * @class Roo.bootstrap.Link
3278  * @extends Roo.bootstrap.Component
3279  * @children Roo.bootstrap.Component
3280  * Bootstrap Link Class (eg. '<a href>')
3281  
3282  * @cfg {String} alt image alternative text
3283  * @cfg {String} href a tag href
3284  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285  * @cfg {String} html the content of the link.
3286  * @cfg {String} anchor name for the anchor link
3287  * @cfg {String} fa - favicon
3288
3289  * @cfg {Boolean} preventDefault (true | false) default false
3290
3291  * 
3292  * @constructor
3293  * Create a new Input
3294  * @param {Object} config The config object
3295  */
3296
3297 Roo.bootstrap.Link = function(config){
3298     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3299     
3300     this.addEvents({
3301         // img events
3302         /**
3303          * @event click
3304          * The img click event for the img.
3305          * @param {Roo.EventObject} e
3306          */
3307         "click" : true
3308     });
3309 };
3310
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3312     
3313     href: false,
3314     target: false,
3315     preventDefault: false,
3316     anchor : false,
3317     alt : false,
3318     fa: false,
3319
3320
3321     getAutoCreate : function()
3322     {
3323         var html = this.html || '';
3324         
3325         if (this.fa !== false) {
3326             html = '<i class="fa fa-' + this.fa + '"></i>';
3327         }
3328         var cfg = {
3329             tag: 'a'
3330         };
3331         // anchor's do not require html/href...
3332         if (this.anchor === false) {
3333             cfg.html = html;
3334             cfg.href = this.href || '#';
3335         } else {
3336             cfg.name = this.anchor;
3337             if (this.html !== false || this.fa !== false) {
3338                 cfg.html = html;
3339             }
3340             if (this.href !== false) {
3341                 cfg.href = this.href;
3342             }
3343         }
3344         
3345         if(this.alt !== false){
3346             cfg.alt = this.alt;
3347         }
3348         
3349         
3350         if(this.target !== false) {
3351             cfg.target = this.target;
3352         }
3353         
3354         return cfg;
3355     },
3356     
3357     initEvents: function() {
3358         
3359         if(!this.href || this.preventDefault){
3360             this.el.on('click', this.onClick, this);
3361         }
3362     },
3363     
3364     onClick : function(e)
3365     {
3366         if(this.preventDefault){
3367             e.preventDefault();
3368         }
3369         //Roo.log('img onclick');
3370         this.fireEvent('click', this, e);
3371     }
3372    
3373 });
3374
3375  /*
3376  * - LGPL
3377  *
3378  * header
3379  * 
3380  */
3381
3382 /**
3383  * @class Roo.bootstrap.Header
3384  * @extends Roo.bootstrap.Component
3385  * @children Roo.bootstrap.Component
3386  * Bootstrap Header class
3387  *
3388  * 
3389  * @cfg {String} html content of header
3390  * @cfg {Number} level (1|2|3|4|5|6) default 1
3391  * 
3392  * @constructor
3393  * Create a new Header
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.Header  = function(config){
3399     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3403     
3404     //href : false,
3405     html : false,
3406     level : 1,
3407     
3408     
3409     
3410     getAutoCreate : function(){
3411         
3412         
3413         
3414         var cfg = {
3415             tag: 'h' + (1 *this.level),
3416             html: this.html || ''
3417         } ;
3418         
3419         return cfg;
3420     }
3421    
3422 });
3423
3424  
3425
3426  /**
3427  * @class Roo.bootstrap.MenuMgr
3428  * @licence LGPL
3429  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430  * @static
3431  */
3432 Roo.bootstrap.menu.Manager = function(){
3433    var menus, active, groups = {}, attached = false, lastShow = new Date();
3434
3435    // private - called when first menu is created
3436    function init(){
3437        menus = {};
3438        active = new Roo.util.MixedCollection();
3439        Roo.get(document).addKeyListener(27, function(){
3440            if(active.length > 0){
3441                hideAll();
3442            }
3443        });
3444    }
3445
3446    // private
3447    function hideAll(){
3448        if(active && active.length > 0){
3449            var c = active.clone();
3450            c.each(function(m){
3451                m.hide();
3452            });
3453        }
3454    }
3455
3456    // private
3457    function onHide(m){
3458        active.remove(m);
3459        if(active.length < 1){
3460            Roo.get(document).un("mouseup", onMouseDown);
3461             
3462            attached = false;
3463        }
3464    }
3465
3466    // private
3467    function onShow(m){
3468        var last = active.last();
3469        lastShow = new Date();
3470        active.add(m);
3471        if(!attached){
3472           Roo.get(document).on("mouseup", onMouseDown);
3473            
3474            attached = true;
3475        }
3476        if(m.parentMenu){
3477           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478           m.parentMenu.activeChild = m;
3479        }else if(last && last.isVisible()){
3480           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3481        }
3482    }
3483
3484    // private
3485    function onBeforeHide(m){
3486        if(m.activeChild){
3487            m.activeChild.hide();
3488        }
3489        if(m.autoHideTimer){
3490            clearTimeout(m.autoHideTimer);
3491            delete m.autoHideTimer;
3492        }
3493    }
3494
3495    // private
3496    function onBeforeShow(m){
3497        var pm = m.parentMenu;
3498        if(!pm && !m.allowOtherMenus){
3499            hideAll();
3500        }else if(pm && pm.activeChild && active != m){
3501            pm.activeChild.hide();
3502        }
3503    }
3504
3505    // private this should really trigger on mouseup..
3506    function onMouseDown(e){
3507         Roo.log("on Mouse Up");
3508         
3509         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510             Roo.log("MenuManager hideAll");
3511             hideAll();
3512             e.stopEvent();
3513         }
3514         
3515         
3516    }
3517
3518    // private
3519    function onBeforeCheck(mi, state){
3520        if(state){
3521            var g = groups[mi.group];
3522            for(var i = 0, l = g.length; i < l; i++){
3523                if(g[i] != mi){
3524                    g[i].setChecked(false);
3525                }
3526            }
3527        }
3528    }
3529
3530    return {
3531
3532        /**
3533         * Hides all menus that are currently visible
3534         */
3535        hideAll : function(){
3536             hideAll();  
3537        },
3538
3539        // private
3540        register : function(menu){
3541            if(!menus){
3542                init();
3543            }
3544            menus[menu.id] = menu;
3545            menu.on("beforehide", onBeforeHide);
3546            menu.on("hide", onHide);
3547            menu.on("beforeshow", onBeforeShow);
3548            menu.on("show", onShow);
3549            var g = menu.group;
3550            if(g && menu.events["checkchange"]){
3551                if(!groups[g]){
3552                    groups[g] = [];
3553                }
3554                groups[g].push(menu);
3555                menu.on("checkchange", onCheck);
3556            }
3557        },
3558
3559         /**
3560          * Returns a {@link Roo.menu.Menu} object
3561          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562          * be used to generate and return a new Menu instance.
3563          */
3564        get : function(menu){
3565            if(typeof menu == "string"){ // menu id
3566                return menus[menu];
3567            }else if(menu.events){  // menu instance
3568                return menu;
3569            }
3570            /*else if(typeof menu.length == 'number'){ // array of menu items?
3571                return new Roo.bootstrap.Menu({items:menu});
3572            }else{ // otherwise, must be a config
3573                return new Roo.bootstrap.Menu(menu);
3574            }
3575            */
3576            return false;
3577        },
3578
3579        // private
3580        unregister : function(menu){
3581            delete menus[menu.id];
3582            menu.un("beforehide", onBeforeHide);
3583            menu.un("hide", onHide);
3584            menu.un("beforeshow", onBeforeShow);
3585            menu.un("show", onShow);
3586            var g = menu.group;
3587            if(g && menu.events["checkchange"]){
3588                groups[g].remove(menu);
3589                menu.un("checkchange", onCheck);
3590            }
3591        },
3592
3593        // private
3594        registerCheckable : function(menuItem){
3595            var g = menuItem.group;
3596            if(g){
3597                if(!groups[g]){
3598                    groups[g] = [];
3599                }
3600                groups[g].push(menuItem);
3601                menuItem.on("beforecheckchange", onBeforeCheck);
3602            }
3603        },
3604
3605        // private
3606        unregisterCheckable : function(menuItem){
3607            var g = menuItem.group;
3608            if(g){
3609                groups[g].remove(menuItem);
3610                menuItem.un("beforecheckchange", onBeforeCheck);
3611            }
3612        }
3613    };
3614 }(); 
3615 /**
3616  * @class Roo.bootstrap.menu.Menu
3617  * @extends Roo.bootstrap.Component
3618  * @licence LGPL
3619  * @children Roo.bootstrap.menu.Item
3620  * @parent none
3621  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3622  * 
3623  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3625  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3626  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3627   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3628   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3629  
3630  * @constructor
3631  * Create a new Menu
3632  * @param {Object} config The config objectQ
3633  */
3634
3635
3636 Roo.bootstrap.menu.Menu = function(config){
3637     
3638     if (config.type == 'treeview') {
3639         // normally menu's are drawn attached to the document to handle layering etc..
3640         // however treeview (used by the docs menu is drawn into the parent element)
3641         this.container_method = 'getChildContainer'; 
3642     }
3643     
3644     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645     if (this.registerMenu && this.type != 'treeview')  {
3646         Roo.bootstrap.menu.Manager.register(this);
3647     }
3648     
3649     
3650     this.addEvents({
3651         /**
3652          * @event beforeshow
3653          * Fires before this menu is displayed (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforeshow : true,
3657         /**
3658          * @event beforehide
3659          * Fires before this menu is hidden (return false to block)
3660          * @param {Roo.menu.Menu} this
3661          */
3662         beforehide : true,
3663         /**
3664          * @event show
3665          * Fires after this menu is displayed
3666          * @param {Roo.menu.Menu} this
3667          */
3668         show : true,
3669         /**
3670          * @event hide
3671          * Fires after this menu is hidden
3672          * @param {Roo.menu.Menu} this
3673          */
3674         hide : true,
3675         /**
3676          * @event click
3677          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680          * @param {Roo.EventObject} e
3681          */
3682         click : true,
3683         /**
3684          * @event mouseover
3685          * Fires when the mouse is hovering over this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseover : true,
3691         /**
3692          * @event mouseout
3693          * Fires when the mouse exits this menu
3694          * @param {Roo.menu.Menu} this
3695          * @param {Roo.EventObject} e
3696          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3697          */
3698         mouseout : true,
3699         /**
3700          * @event itemclick
3701          * Fires when a menu item contained in this menu is clicked
3702          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703          * @param {Roo.EventObject} e
3704          */
3705         itemclick: true
3706     });
3707     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 };
3709
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3711     
3712    /// html : false,
3713    
3714     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3715     type: false,
3716     /**
3717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718      */
3719     registerMenu : true,
3720     
3721     menuItems :false, // stores the menu items..
3722     
3723     hidden:true,
3724         
3725     parentMenu : false,
3726     
3727     stopEvent : true,
3728     
3729     isLink : false,
3730     
3731     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732     
3733     hideTrigger : false,
3734     
3735     align : 'tl-bl?',
3736     
3737     
3738     getChildContainer : function() {
3739         return this.el;  
3740     },
3741     
3742     getAutoCreate : function(){
3743          
3744         //if (['right'].indexOf(this.align)!==-1) {
3745         //    cfg.cn[1].cls += ' pull-right'
3746         //}
3747          
3748         var cfg = {
3749             tag : 'ul',
3750             cls : 'dropdown-menu shadow' ,
3751             style : 'z-index:1000'
3752             
3753         };
3754         
3755         if (this.type === 'submenu') {
3756             cfg.cls = 'submenu active';
3757         }
3758         if (this.type === 'treeview') {
3759             cfg.cls = 'treeview-menu';
3760         }
3761         
3762         return cfg;
3763     },
3764     initEvents : function() {
3765         
3766        // Roo.log("ADD event");
3767        // Roo.log(this.triggerEl.dom);
3768         if (this.triggerEl) {
3769             
3770             this.triggerEl.on('click', this.onTriggerClick, this);
3771             
3772             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773             
3774             if (!this.hideTrigger) {
3775                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776                     // dropdown toggle on the 'a' in BS4?
3777                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778                 } else {
3779                     this.triggerEl.addClass('dropdown-toggle');
3780                 }
3781             }
3782         }
3783         
3784         if (Roo.isTouch) {
3785             this.el.on('touchstart'  , this.onTouch, this);
3786         }
3787         this.el.on('click' , this.onClick, this);
3788
3789         this.el.on("mouseover", this.onMouseOver, this);
3790         this.el.on("mouseout", this.onMouseOut, this);
3791         
3792     },
3793     
3794     findTargetItem : function(e)
3795     {
3796         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3797         if(!t){
3798             return false;
3799         }
3800         //Roo.log(t);         Roo.log(t.id);
3801         if(t && t.id){
3802             //Roo.log(this.menuitems);
3803             return this.menuitems.get(t.id);
3804             
3805             //return this.items.get(t.menuItemId);
3806         }
3807         
3808         return false;
3809     },
3810     
3811     onTouch : function(e) 
3812     {
3813         Roo.log("menu.onTouch");
3814         //e.stopEvent(); this make the user popdown broken
3815         this.onClick(e);
3816     },
3817     
3818     onClick : function(e)
3819     {
3820         Roo.log("menu.onClick");
3821         
3822         var t = this.findTargetItem(e);
3823         if(!t || t.isContainer){
3824             return;
3825         }
3826         Roo.log(e);
3827         /*
3828         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3829             if(t == this.activeItem && t.shouldDeactivate(e)){
3830                 this.activeItem.deactivate();
3831                 delete this.activeItem;
3832                 return;
3833             }
3834             if(t.canActivate){
3835                 this.setActiveItem(t, true);
3836             }
3837             return;
3838             
3839             
3840         }
3841         */
3842        
3843         Roo.log('pass click event');
3844         
3845         t.onClick(e);
3846         
3847         this.fireEvent("click", this, t, e);
3848         
3849         var _this = this;
3850         
3851         if(!t.href.length || t.href == '#'){
3852             (function() { _this.hide(); }).defer(100);
3853         }
3854         
3855     },
3856     
3857     onMouseOver : function(e){
3858         var t  = this.findTargetItem(e);
3859         //Roo.log(t);
3860         //if(t){
3861         //    if(t.canActivate && !t.disabled){
3862         //        this.setActiveItem(t, true);
3863         //    }
3864         //}
3865         
3866         this.fireEvent("mouseover", this, e, t);
3867     },
3868     isVisible : function(){
3869         return !this.hidden;
3870     },
3871     onMouseOut : function(e){
3872         var t  = this.findTargetItem(e);
3873         
3874         //if(t ){
3875         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3876         //        this.activeItem.deactivate();
3877         //        delete this.activeItem;
3878         //    }
3879         //}
3880         this.fireEvent("mouseout", this, e, t);
3881     },
3882     
3883     
3884     /**
3885      * Displays this menu relative to another element
3886      * @param {String/HTMLElement/Roo.Element} element The element to align to
3887      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888      * the element (defaults to this.defaultAlign)
3889      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890      */
3891     show : function(el, pos, parentMenu)
3892     {
3893         if (false === this.fireEvent("beforeshow", this)) {
3894             Roo.log("show canceled");
3895             return;
3896         }
3897         this.parentMenu = parentMenu;
3898         if(!this.el){
3899             this.render();
3900         }
3901         this.el.addClass('show'); // show otherwise we do not know how big we are..
3902          
3903         var xy = this.el.getAlignToXY(el, pos);
3904         
3905         // bl-tl << left align  below
3906         // tl-bl << left align 
3907         
3908         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909             // if it goes to far to the right.. -> align left.
3910             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911         }
3912         if(xy[0] < 0){
3913             // was left align - go right?
3914             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915         }
3916         
3917         // goes down the bottom
3918         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919            xy[1]  < 0 ){
3920             var a = this.align.replace('?', '').split('-');
3921             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3922             
3923         }
3924         
3925         this.showAt(  xy , parentMenu, false);
3926     },
3927      /**
3928      * Displays this menu at a specific xy position
3929      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931      */
3932     showAt : function(xy, parentMenu, /* private: */_e){
3933         this.parentMenu = parentMenu;
3934         if(!this.el){
3935             this.render();
3936         }
3937         if(_e !== false){
3938             this.fireEvent("beforeshow", this);
3939             //xy = this.el.adjustForConstraints(xy);
3940         }
3941         
3942         //this.el.show();
3943         this.hideMenuItems();
3944         this.hidden = false;
3945         if (this.triggerEl) {
3946             this.triggerEl.addClass('open');
3947         }
3948         
3949         this.el.addClass('show');
3950         
3951         
3952         
3953         // reassign x when hitting right
3954         
3955         // reassign y when hitting bottom
3956         
3957         // but the list may align on trigger left or trigger top... should it be a properity?
3958         
3959         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3960             this.el.setXY(xy);
3961         }
3962         
3963         this.focus();
3964         this.fireEvent("show", this);
3965     },
3966     
3967     focus : function(){
3968         return;
3969         if(!this.hidden){
3970             this.doFocus.defer(50, this);
3971         }
3972     },
3973
3974     doFocus : function(){
3975         if(!this.hidden){
3976             this.focusEl.focus();
3977         }
3978     },
3979
3980     /**
3981      * Hides this menu and optionally all parent menus
3982      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983      */
3984     hide : function(deep)
3985     {
3986         if (false === this.fireEvent("beforehide", this)) {
3987             Roo.log("hide canceled");
3988             return;
3989         }
3990         this.hideMenuItems();
3991         if(this.el && this.isVisible()){
3992            
3993             if(this.activeItem){
3994                 this.activeItem.deactivate();
3995                 this.activeItem = null;
3996             }
3997             if (this.triggerEl) {
3998                 this.triggerEl.removeClass('open');
3999             }
4000             
4001             this.el.removeClass('show');
4002             this.hidden = true;
4003             this.fireEvent("hide", this);
4004         }
4005         if(deep === true && this.parentMenu){
4006             this.parentMenu.hide(true);
4007         }
4008     },
4009     
4010     onTriggerClick : function(e)
4011     {
4012         Roo.log('trigger click');
4013         
4014         var target = e.getTarget();
4015         
4016         Roo.log(target.nodeName.toLowerCase());
4017         
4018         if(target.nodeName.toLowerCase() === 'i'){
4019             e.preventDefault();
4020         }
4021         
4022     },
4023     
4024     onTriggerPress  : function(e)
4025     {
4026         Roo.log('trigger press');
4027         //Roo.log(e.getTarget());
4028        // Roo.log(this.triggerEl.dom);
4029        
4030         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031         var pel = Roo.get(e.getTarget());
4032         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033             Roo.log('is treeview or dropdown?');
4034             return;
4035         }
4036         
4037         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038             return;
4039         }
4040         
4041         if (this.isVisible()) {
4042             Roo.log('hide');
4043             this.hide();
4044         } else {
4045             Roo.log('show');
4046             
4047             this.show(this.triggerEl, this.align, false);
4048         }
4049         
4050         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4051             e.stopEvent();
4052         }
4053         
4054     },
4055        
4056     
4057     hideMenuItems : function()
4058     {
4059         Roo.log("hide Menu Items");
4060         if (!this.el) { 
4061             return;
4062         }
4063         
4064         this.el.select('.open',true).each(function(aa) {
4065             
4066             aa.removeClass('open');
4067          
4068         });
4069     },
4070     addxtypeChild : function (tree, cntr) {
4071         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072           
4073         this.menuitems.add(comp);
4074         return comp;
4075
4076     },
4077     getEl : function()
4078     {
4079         Roo.log(this.el);
4080         return this.el;
4081     },
4082     
4083     clear : function()
4084     {
4085         this.getEl().dom.innerHTML = '';
4086         this.menuitems.clear();
4087     }
4088 });
4089
4090  
4091  /**
4092  * @class Roo.bootstrap.menu.Item
4093  * @extends Roo.bootstrap.Component
4094  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095  * @parent Roo.bootstrap.menu.Menu
4096  * @licence LGPL
4097  * Bootstrap MenuItem class
4098  * 
4099  * @cfg {String} html the menu label
4100  * @cfg {String} href the link
4101  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4104  * @cfg {String} fa favicon to show on left of menu item.
4105  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4106  * 
4107  * 
4108  * @constructor
4109  * Create a new MenuItem
4110  * @param {Object} config The config object
4111  */
4112
4113
4114 Roo.bootstrap.menu.Item = function(config){
4115     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4116     this.addEvents({
4117         // raw events
4118         /**
4119          * @event click
4120          * The raw click event for the entire grid.
4121          * @param {Roo.bootstrap.menu.Item} this
4122          * @param {Roo.EventObject} e
4123          */
4124         "click" : true
4125     });
4126 };
4127
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4129     
4130     href : false,
4131     html : false,
4132     preventDefault: false,
4133     isContainer : false,
4134     active : false,
4135     fa: false,
4136     
4137     getAutoCreate : function(){
4138         
4139         if(this.isContainer){
4140             return {
4141                 tag: 'li',
4142                 cls: 'dropdown-menu-item '
4143             };
4144         }
4145         var ctag = {
4146             tag: 'span',
4147             html: 'Link'
4148         };
4149         
4150         var anc = {
4151             tag : 'a',
4152             cls : 'dropdown-item',
4153             href : '#',
4154             cn : [  ]
4155         };
4156         
4157         if (this.fa !== false) {
4158             anc.cn.push({
4159                 tag : 'i',
4160                 cls : 'fa fa-' + this.fa
4161             });
4162         }
4163         
4164         anc.cn.push(ctag);
4165         
4166         
4167         var cfg= {
4168             tag: 'li',
4169             cls: 'dropdown-menu-item',
4170             cn: [ anc ]
4171         };
4172         if (this.parent().type == 'treeview') {
4173             cfg.cls = 'treeview-menu';
4174         }
4175         if (this.active) {
4176             cfg.cls += ' active';
4177         }
4178         
4179         
4180         
4181         anc.href = this.href || cfg.cn[0].href ;
4182         ctag.html = this.html || cfg.cn[0].html ;
4183         return cfg;
4184     },
4185     
4186     initEvents: function()
4187     {
4188         if (this.parent().type == 'treeview') {
4189             this.el.select('a').on('click', this.onClick, this);
4190         }
4191         
4192         if (this.menu) {
4193             this.menu.parentType = this.xtype;
4194             this.menu.triggerEl = this.el;
4195             this.menu = this.addxtype(Roo.apply({}, this.menu));
4196         }
4197         
4198     },
4199     onClick : function(e)
4200     {
4201         Roo.log('item on click ');
4202         
4203         if(this.preventDefault){
4204             e.preventDefault();
4205         }
4206         //this.parent().hideMenuItems();
4207         
4208         this.fireEvent('click', this, e);
4209     },
4210     getEl : function()
4211     {
4212         return this.el;
4213     } 
4214 });
4215
4216  
4217
4218  
4219
4220   
4221 /**
4222  * @class Roo.bootstrap.menu.Separator
4223  * @extends Roo.bootstrap.Component
4224  * @licence LGPL
4225  * @parent Roo.bootstrap.menu.Menu
4226  * Bootstrap Separator class
4227  * 
4228  * @constructor
4229  * Create a new Separator
4230  * @param {Object} config The config object
4231  */
4232
4233
4234 Roo.bootstrap.menu.Separator = function(config){
4235     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 };
4237
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4239     
4240     getAutoCreate : function(){
4241         var cfg = {
4242             tag : 'li',
4243             cls: 'dropdown-divider divider'
4244         };
4245         
4246         return cfg;
4247     }
4248    
4249 });
4250
4251  
4252
4253  
4254 /*
4255 * Licence: LGPL
4256 */
4257
4258 /**
4259  * @class Roo.bootstrap.Modal
4260  * @extends Roo.bootstrap.Component
4261  * @parent none builder
4262  * @children Roo.bootstrap.Component
4263  * Bootstrap Modal class
4264  * @cfg {String} title Title of dialog
4265  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4267  * @cfg {Boolean} specificTitle default false
4268  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270  * @cfg {Boolean} animate default true
4271  * @cfg {Boolean} allow_close default true
4272  * @cfg {Boolean} fitwindow default false
4273  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276  * @cfg {String} size (sm|lg|xl) default empty
4277  * @cfg {Number} max_width set the max width of modal
4278  * @cfg {Boolean} editableTitle can the title be edited
4279
4280  *
4281  *
4282  * @constructor
4283  * Create a new Modal Dialog
4284  * @param {Object} config The config object
4285  */
4286
4287 Roo.bootstrap.Modal = function(config){
4288     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4289     this.addEvents({
4290         // raw events
4291         /**
4292          * @event btnclick
4293          * The raw btnclick event for the button
4294          * @param {Roo.EventObject} e
4295          */
4296         "btnclick" : true,
4297         /**
4298          * @event resize
4299          * Fire when dialog resize
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} e
4302          */
4303         "resize" : true,
4304         /**
4305          * @event titlechanged
4306          * Fire when the editable title has been changed
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} value
4309          */
4310         "titlechanged" : true 
4311         
4312     });
4313     this.buttons = this.buttons || [];
4314
4315     if (this.tmpl) {
4316         this.tmpl = Roo.factory(this.tmpl);
4317     }
4318
4319 };
4320
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4322
4323     title : 'test dialog',
4324
4325     buttons : false,
4326
4327     // set on load...
4328
4329     html: false,
4330
4331     tmp: false,
4332
4333     specificTitle: false,
4334
4335     buttonPosition: 'right',
4336
4337     allow_close : true,
4338
4339     animate : true,
4340
4341     fitwindow: false,
4342     
4343      // private
4344     dialogEl: false,
4345     bodyEl:  false,
4346     footerEl:  false,
4347     titleEl:  false,
4348     closeEl:  false,
4349
4350     size: '',
4351     
4352     max_width: 0,
4353     
4354     max_height: 0,
4355     
4356     fit_content: false,
4357     editableTitle  : false,
4358
4359     onRender : function(ct, position)
4360     {
4361         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362
4363         if(!this.el){
4364             var cfg = Roo.apply({},  this.getAutoCreate());
4365             cfg.id = Roo.id();
4366             //if(!cfg.name){
4367             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368             //}
4369             //if (!cfg.name.length) {
4370             //    delete cfg.name;
4371            // }
4372             if (this.cls) {
4373                 cfg.cls += ' ' + this.cls;
4374             }
4375             if (this.style) {
4376                 cfg.style = this.style;
4377             }
4378             this.el = Roo.get(document.body).createChild(cfg, position);
4379         }
4380         //var type = this.el.dom.type;
4381
4382
4383         if(this.tabIndex !== undefined){
4384             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385         }
4386
4387         this.dialogEl = this.el.select('.modal-dialog',true).first();
4388         this.bodyEl = this.el.select('.modal-body',true).first();
4389         this.closeEl = this.el.select('.modal-header .close', true).first();
4390         this.headerEl = this.el.select('.modal-header',true).first();
4391         this.titleEl = this.el.select('.modal-title',true).first();
4392         this.footerEl = this.el.select('.modal-footer',true).first();
4393
4394         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395         
4396         //this.el.addClass("x-dlg-modal");
4397
4398         if (this.buttons.length) {
4399             Roo.each(this.buttons, function(bb) {
4400                 var b = Roo.apply({}, bb);
4401                 b.xns = b.xns || Roo.bootstrap;
4402                 b.xtype = b.xtype || 'Button';
4403                 if (typeof(b.listeners) == 'undefined') {
4404                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4405                 }
4406
4407                 var btn = Roo.factory(b);
4408
4409                 btn.render(this.getButtonContainer());
4410
4411             },this);
4412         }
4413         // render the children.
4414         var nitems = [];
4415
4416         if(typeof(this.items) != 'undefined'){
4417             var items = this.items;
4418             delete this.items;
4419
4420             for(var i =0;i < items.length;i++) {
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628     },
4629
4630     show : function() {
4631
4632         if (!this.rendered) {
4633             this.render();
4634         }
4635         this.toggleHeaderInput(false);
4636         //this.el.setStyle('display', 'block');
4637         this.el.removeClass('hideing');
4638         this.el.dom.style.display='block';
4639         
4640         Roo.get(document.body).addClass('modal-open');
4641  
4642         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4643             
4644             (function(){
4645                 this.el.addClass('show');
4646                 this.el.addClass('in');
4647             }).defer(50, this);
4648         }else{
4649             this.el.addClass('show');
4650             this.el.addClass('in');
4651         }
4652
4653         // not sure how we can show data in here..
4654         //if (this.tmpl) {
4655         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4656         //}
4657
4658         Roo.get(document.body).addClass("x-body-masked");
4659         
4660         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4661         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662         this.maskEl.dom.style.display = 'block';
4663         this.maskEl.addClass('show');
4664         
4665         
4666         this.resize();
4667         
4668         this.fireEvent('show', this);
4669
4670         // set zindex here - otherwise it appears to be ignored...
4671         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672
4673         (function () {
4674             this.items.forEach( function(e) {
4675                 e.layout ? e.layout() : false;
4676
4677             });
4678         }).defer(100,this);
4679
4680     },
4681     hide : function()
4682     {
4683         if(this.fireEvent("beforehide", this) !== false){
4684             
4685             this.maskEl.removeClass('show');
4686             
4687             this.maskEl.dom.style.display = '';
4688             Roo.get(document.body).removeClass("x-body-masked");
4689             this.el.removeClass('in');
4690             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4691
4692             if(this.animate){ // why
4693                 this.el.addClass('hideing');
4694                 this.el.removeClass('show');
4695                 (function(){
4696                     if (!this.el.hasClass('hideing')) {
4697                         return; // it's been shown again...
4698                     }
4699                     
4700                     this.el.dom.style.display='';
4701
4702                     Roo.get(document.body).removeClass('modal-open');
4703                     this.el.removeClass('hideing');
4704                 }).defer(150,this);
4705                 
4706             }else{
4707                 this.el.removeClass('show');
4708                 this.el.dom.style.display='';
4709                 Roo.get(document.body).removeClass('modal-open');
4710
4711             }
4712             this.fireEvent('hide', this);
4713         }
4714     },
4715     isVisible : function()
4716     {
4717         
4718         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4719         
4720     },
4721
4722     addButton : function(str, cb)
4723     {
4724
4725
4726         var b = Roo.apply({}, { html : str } );
4727         b.xns = b.xns || Roo.bootstrap;
4728         b.xtype = b.xtype || 'Button';
4729         if (typeof(b.listeners) == 'undefined') {
4730             b.listeners = { click : cb.createDelegate(this)  };
4731         }
4732
4733         var btn = Roo.factory(b);
4734
4735         btn.render(this.getButtonContainer());
4736
4737         return btn;
4738
4739     },
4740
4741     setDefaultButton : function(btn)
4742     {
4743         //this.el.select('.modal-footer').()
4744     },
4745
4746     resizeTo: function(w,h)
4747     {
4748         this.dialogEl.setWidth(w);
4749         
4750         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4751
4752         this.bodyEl.setHeight(h - diff);
4753         
4754         this.fireEvent('resize', this);
4755     },
4756     
4757     setContentSize  : function(w, h)
4758     {
4759
4760     },
4761     onButtonClick: function(btn,e)
4762     {
4763         //Roo.log([a,b,c]);
4764         this.fireEvent('btnclick', btn.name, e);
4765     },
4766      /**
4767      * Set the title of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setTitle: function(str) {
4771         this.titleEl.dom.innerHTML = str;
4772         this.title = str;
4773     },
4774     /**
4775      * Set the body of the Dialog
4776      * @param {String} str new Title
4777      */
4778     setBody: function(str) {
4779         this.bodyEl.dom.innerHTML = str;
4780     },
4781     /**
4782      * Set the body of the Dialog using the template
4783      * @param {Obj} data - apply this data to the template and replace the body contents.
4784      */
4785     applyBody: function(obj)
4786     {
4787         if (!this.tmpl) {
4788             Roo.log("Error - using apply Body without a template");
4789             //code
4790         }
4791         this.tmpl.overwrite(this.bodyEl, obj);
4792     },
4793     
4794     getChildHeight : function(child_nodes)
4795     {
4796         if(
4797             !child_nodes ||
4798             child_nodes.length == 0
4799         ) {
4800             return 0;
4801         }
4802         
4803         var child_height = 0;
4804         
4805         for(var i = 0; i < child_nodes.length; i++) {
4806             
4807             /*
4808             * for modal with tabs...
4809             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4810                 
4811                 var layout_childs = child_nodes[i].childNodes;
4812                 
4813                 for(var j = 0; j < layout_childs.length; j++) {
4814                     
4815                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4816                         
4817                         var layout_body_childs = layout_childs[j].childNodes;
4818                         
4819                         for(var k = 0; k < layout_body_childs.length; k++) {
4820                             
4821                             if(layout_body_childs[k].classList.contains('navbar')) {
4822                                 child_height += layout_body_childs[k].offsetHeight;
4823                                 continue;
4824                             }
4825                             
4826                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4827                                 
4828                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4829                                 
4830                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4831                                     
4832                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4834                                         continue;
4835                                     }
4836                                     
4837                                 }
4838                                 
4839                             }
4840                             
4841                         }
4842                     }
4843                 }
4844                 continue;
4845             }
4846             */
4847             
4848             child_height += child_nodes[i].offsetHeight;
4849             // Roo.log(child_nodes[i].offsetHeight);
4850         }
4851         
4852         return child_height;
4853     },
4854     toggleHeaderInput : function(is_edit)
4855     {
4856         if (!this.editableTitle) {
4857             return; // not editable.
4858         }
4859         if (is_edit && this.is_header_editing) {
4860             return; // already editing..
4861         }
4862         if (is_edit) {
4863     
4864             this.headerEditEl.dom.value = this.title;
4865             this.headerEditEl.removeClass('d-none');
4866             this.headerEditEl.dom.focus();
4867             this.titleEl.addClass('d-none');
4868             
4869             this.is_header_editing = true;
4870             return
4871         }
4872         // flip back to not editing.
4873         this.title = this.headerEditEl.dom.value;
4874         this.headerEditEl.addClass('d-none');
4875         this.titleEl.removeClass('d-none');
4876         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877         this.is_header_editing = false;
4878         this.fireEvent('titlechanged', this, this.title);
4879     
4880             
4881         
4882     }
4883
4884 });
4885
4886
4887 Roo.apply(Roo.bootstrap.Modal,  {
4888     /**
4889          * Button config that displays a single OK button
4890          * @type Object
4891          */
4892         OK :  [{
4893             name : 'ok',
4894             weight : 'primary',
4895             html : 'OK'
4896         }],
4897         /**
4898          * Button config that displays Yes and No buttons
4899          * @type Object
4900          */
4901         YESNO : [
4902             {
4903                 name  : 'no',
4904                 html : 'No'
4905             },
4906             {
4907                 name  :'yes',
4908                 weight : 'primary',
4909                 html : 'Yes'
4910             }
4911         ],
4912
4913         /**
4914          * Button config that displays OK and Cancel buttons
4915          * @type Object
4916          */
4917         OKCANCEL : [
4918             {
4919                name : 'cancel',
4920                 html : 'Cancel'
4921             },
4922             {
4923                 name : 'ok',
4924                 weight : 'primary',
4925                 html : 'OK'
4926             }
4927         ],
4928         /**
4929          * Button config that displays Yes, No and Cancel buttons
4930          * @type Object
4931          */
4932         YESNOCANCEL : [
4933             {
4934                 name : 'yes',
4935                 weight : 'primary',
4936                 html : 'Yes'
4937             },
4938             {
4939                 name : 'no',
4940                 html : 'No'
4941             },
4942             {
4943                 name : 'cancel',
4944                 html : 'Cancel'
4945             }
4946         ],
4947         
4948         zIndex : 10001
4949 });
4950
4951 /*
4952  * - LGPL
4953  *
4954  * messagebox - can be used as a replace
4955  * 
4956  */
4957 /**
4958  * @class Roo.MessageBox
4959  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4960  * Example usage:
4961  *<pre><code>
4962 // Basic alert:
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4964
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4967     if (btn == 'ok'){
4968         // process text value...
4969     }
4970 });
4971
4972 // Show a dialog using config options:
4973 Roo.Msg.show({
4974    title:'Save Changes?',
4975    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976    buttons: Roo.Msg.YESNOCANCEL,
4977    fn: processResult,
4978    animEl: 'elId'
4979 });
4980 </code></pre>
4981  * @static
4982  */
4983 Roo.bootstrap.MessageBox = function(){
4984     var dlg, opt, mask, waitTimer;
4985     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986     var buttons, activeTextEl, bwidth;
4987
4988     
4989     // private
4990     var handleButton = function(button){
4991         dlg.hide();
4992         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4993     };
4994
4995     // private
4996     var handleHide = function(){
4997         if(opt && opt.cls){
4998             dlg.el.removeClass(opt.cls);
4999         }
5000         //if(waitTimer){
5001         //    Roo.TaskMgr.stop(waitTimer);
5002         //    waitTimer = null;
5003         //}
5004     };
5005
5006     // private
5007     var updateButtons = function(b){
5008         var width = 0;
5009         if(!b){
5010             buttons["ok"].hide();
5011             buttons["cancel"].hide();
5012             buttons["yes"].hide();
5013             buttons["no"].hide();
5014             dlg.footerEl.hide();
5015             
5016             return width;
5017         }
5018         dlg.footerEl.show();
5019         for(var k in buttons){
5020             if(typeof buttons[k] != "function"){
5021                 if(b[k]){
5022                     buttons[k].show();
5023                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024                     width += buttons[k].el.getWidth()+15;
5025                 }else{
5026                     buttons[k].hide();
5027                 }
5028             }
5029         }
5030         return width;
5031     };
5032
5033     // private
5034     var handleEsc = function(d, k, e){
5035         if(opt && opt.closable !== false){
5036             dlg.hide();
5037         }
5038         if(e){
5039             e.stopEvent();
5040         }
5041     };
5042
5043     return {
5044         /**
5045          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046          * @return {Roo.BasicDialog} The BasicDialog element
5047          */
5048         getDialog : function(){
5049            if(!dlg){
5050                 dlg = new Roo.bootstrap.Modal( {
5051                     //draggable: true,
5052                     //resizable:false,
5053                     //constraintoviewport:false,
5054                     //fixedcenter:true,
5055                     //collapsible : false,
5056                     //shim:true,
5057                     //modal: true,
5058                 //    width: 'auto',
5059                   //  height:100,
5060                     //buttonAlign:"center",
5061                     closeClick : function(){
5062                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5063                             handleButton("no");
5064                         }else{
5065                             handleButton("cancel");
5066                         }
5067                     }
5068                 });
5069                 dlg.render();
5070                 dlg.on("hide", handleHide);
5071                 mask = dlg.mask;
5072                 //dlg.addKeyListener(27, handleEsc);
5073                 buttons = {};
5074                 this.buttons = buttons;
5075                 var bt = this.buttonText;
5076                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5080                 //Roo.log(buttons);
5081                 bodyEl = dlg.bodyEl.createChild({
5082
5083                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084                         '<textarea class="roo-mb-textarea"></textarea>' +
5085                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5086                 });
5087                 msgEl = bodyEl.dom.firstChild;
5088                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089                 textboxEl.enableDisplayMode();
5090                 textboxEl.addKeyListener([10,13], function(){
5091                     if(dlg.isVisible() && opt && opt.buttons){
5092                         if(opt.buttons.ok){
5093                             handleButton("ok");
5094                         }else if(opt.buttons.yes){
5095                             handleButton("yes");
5096                         }
5097                     }
5098                 });
5099                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100                 textareaEl.enableDisplayMode();
5101                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102                 progressEl.enableDisplayMode();
5103                 
5104                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105                 var pf = progressEl.dom.firstChild;
5106                 if (pf) {
5107                     pp = Roo.get(pf.firstChild);
5108                     pp.setHeight(pf.offsetHeight);
5109                 }
5110                 
5111             }
5112             return dlg;
5113         },
5114
5115         /**
5116          * Updates the message box body text
5117          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118          * the XHTML-compliant non-breaking space character '&amp;#160;')
5119          * @return {Roo.MessageBox} This message box
5120          */
5121         updateText : function(text)
5122         {
5123             if(!dlg.isVisible() && !opt.width){
5124                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5126             }
5127             msgEl.innerHTML = text || '&#160;';
5128       
5129             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5131             var w = Math.max(
5132                     Math.min(opt.width || cw , this.maxWidth), 
5133                     Math.max(opt.minWidth || this.minWidth, bwidth)
5134             );
5135             if(opt.prompt){
5136                 activeTextEl.setWidth(w);
5137             }
5138             if(dlg.isVisible()){
5139                 dlg.fixedcenter = false;
5140             }
5141             // to big, make it scroll. = But as usual stupid IE does not support
5142             // !important..
5143             
5144             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5147             } else {
5148                 bodyEl.dom.style.height = '';
5149                 bodyEl.dom.style.overflowY = '';
5150             }
5151             if (cw > w) {
5152                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5153             } else {
5154                 bodyEl.dom.style.overflowX = '';
5155             }
5156             
5157             dlg.setContentSize(w, bodyEl.getHeight());
5158             if(dlg.isVisible()){
5159                 dlg.fixedcenter = true;
5160             }
5161             return this;
5162         },
5163
5164         /**
5165          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5166          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169          * @return {Roo.MessageBox} This message box
5170          */
5171         updateProgress : function(value, text){
5172             if(text){
5173                 this.updateText(text);
5174             }
5175             
5176             if (pp) { // weird bug on my firefox - for some reason this is not defined
5177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5179             }
5180             return this;
5181         },        
5182
5183         /**
5184          * Returns true if the message box is currently displayed
5185          * @return {Boolean} True if the message box is visible, else false
5186          */
5187         isVisible : function(){
5188             return dlg && dlg.isVisible();  
5189         },
5190
5191         /**
5192          * Hides the message box if it is displayed
5193          */
5194         hide : function(){
5195             if(this.isVisible()){
5196                 dlg.hide();
5197             }  
5198         },
5199
5200         /**
5201          * Displays a new message box, or reinitializes an existing message box, based on the config options
5202          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203          * The following config object properties are supported:
5204          * <pre>
5205 Property    Type             Description
5206 ----------  ---------------  ------------------------------------------------------------------------------------
5207 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5208                                    closes (defaults to undefined)
5209 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5212                                    progress and wait dialogs will ignore this property and always hide the
5213                                    close button as they can only be closed programmatically.
5214 cls               String           A custom CSS class to apply to the message box element
5215 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5216                                    displayed (defaults to 75)
5217 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5218                                    function will be btn (the name of the button that was clicked, if applicable,
5219                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5220                                    Progress and wait dialogs will ignore this option since they do not respond to
5221                                    user actions and can only be closed programmatically, so any required function
5222                                    should be called by the same code after it closes the dialog.
5223 icon              String           A CSS class that provides a background image to be used as an icon for
5224                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5226 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5227 modal             Boolean          False to allow user interaction with the page while the message box is
5228                                    displayed (defaults to true)
5229 msg               String           A string that will replace the existing message box body text (defaults
5230                                    to the XHTML-compliant non-breaking space character '&#160;')
5231 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5232 progress          Boolean          True to display a progress bar (defaults to false)
5233 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5236 title             String           The title text
5237 value             String           The string value to set into the active textbox element if displayed
5238 wait              Boolean          True to display a progress bar (defaults to false)
5239 width             Number           The width of the dialog in pixels
5240 </pre>
5241          *
5242          * Example usage:
5243          * <pre><code>
5244 Roo.Msg.show({
5245    title: 'Address',
5246    msg: 'Please enter your address:',
5247    width: 300,
5248    buttons: Roo.MessageBox.OKCANCEL,
5249    multiline: true,
5250    fn: saveAddress,
5251    animEl: 'addAddressBtn'
5252 });
5253 </code></pre>
5254          * @param {Object} config Configuration options
5255          * @return {Roo.MessageBox} This message box
5256          */
5257         show : function(options)
5258         {
5259             
5260             // this causes nightmares if you show one dialog after another
5261             // especially on callbacks..
5262              
5263             if(this.isVisible()){
5264                 
5265                 this.hide();
5266                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5268                 Roo.log("New Dialog Message:" +  options.msg )
5269                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5271                 
5272             }
5273             var d = this.getDialog();
5274             opt = options;
5275             d.setTitle(opt.title || "&#160;");
5276             d.closeEl.setDisplayed(opt.closable !== false);
5277             activeTextEl = textboxEl;
5278             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5279             if(opt.prompt){
5280                 if(opt.multiline){
5281                     textboxEl.hide();
5282                     textareaEl.show();
5283                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5284                         opt.multiline : this.defaultTextHeight);
5285                     activeTextEl = textareaEl;
5286                 }else{
5287                     textboxEl.show();
5288                     textareaEl.hide();
5289                 }
5290             }else{
5291                 textboxEl.hide();
5292                 textareaEl.hide();
5293             }
5294             progressEl.setDisplayed(opt.progress === true);
5295             if (opt.progress) {
5296                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5297             }
5298             this.updateProgress(0);
5299             activeTextEl.dom.value = opt.value || "";
5300             if(opt.prompt){
5301                 dlg.setDefaultButton(activeTextEl);
5302             }else{
5303                 var bs = opt.buttons;
5304                 var db = null;
5305                 if(bs && bs.ok){
5306                     db = buttons["ok"];
5307                 }else if(bs && bs.yes){
5308                     db = buttons["yes"];
5309                 }
5310                 dlg.setDefaultButton(db);
5311             }
5312             bwidth = updateButtons(opt.buttons);
5313             this.updateText(opt.msg);
5314             if(opt.cls){
5315                 d.el.addClass(opt.cls);
5316             }
5317             d.proxyDrag = opt.proxyDrag === true;
5318             d.modal = opt.modal !== false;
5319             d.mask = opt.modal !== false ? mask : false;
5320             if(!d.isVisible()){
5321                 // force it to the end of the z-index stack so it gets a cursor in FF
5322                 document.body.appendChild(dlg.el.dom);
5323                 d.animateTarget = null;
5324                 d.show(options.animEl);
5325             }
5326             return this;
5327         },
5328
5329         /**
5330          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5331          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332          * and closing the message box when the process is complete.
5333          * @param {String} title The title bar text
5334          * @param {String} msg The message box body text
5335          * @return {Roo.MessageBox} This message box
5336          */
5337         progress : function(title, msg){
5338             this.show({
5339                 title : title,
5340                 msg : msg,
5341                 buttons: false,
5342                 progress:true,
5343                 closable:false,
5344                 minWidth: this.minProgressWidth,
5345                 modal : true
5346             });
5347             return this;
5348         },
5349
5350         /**
5351          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352          * If a callback function is passed it will be called after the user clicks the button, and the
5353          * id of the button that was clicked will be passed as the only parameter to the callback
5354          * (could also be the top-right close button).
5355          * @param {String} title The title bar text
5356          * @param {String} msg The message box body text
5357          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358          * @param {Object} scope (optional) The scope of the callback function
5359          * @return {Roo.MessageBox} This message box
5360          */
5361         alert : function(title, msg, fn, scope)
5362         {
5363             this.show({
5364                 title : title,
5365                 msg : msg,
5366                 buttons: this.OK,
5367                 fn: fn,
5368                 closable : false,
5369                 scope : scope,
5370                 modal : true
5371             });
5372             return this;
5373         },
5374
5375         /**
5376          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5377          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378          * You are responsible for closing the message box when the process is complete.
5379          * @param {String} msg The message box body text
5380          * @param {String} title (optional) The title bar text
5381          * @return {Roo.MessageBox} This message box
5382          */
5383         wait : function(msg, title){
5384             this.show({
5385                 title : title,
5386                 msg : msg,
5387                 buttons: false,
5388                 closable:false,
5389                 progress:true,
5390                 modal:true,
5391                 width:300,
5392                 wait:true
5393             });
5394             waitTimer = Roo.TaskMgr.start({
5395                 run: function(i){
5396                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5397                 },
5398                 interval: 1000
5399             });
5400             return this;
5401         },
5402
5403         /**
5404          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407          * @param {String} title The title bar text
5408          * @param {String} msg The message box body text
5409          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410          * @param {Object} scope (optional) The scope of the callback function
5411          * @return {Roo.MessageBox} This message box
5412          */
5413         confirm : function(title, msg, fn, scope){
5414             this.show({
5415                 title : title,
5416                 msg : msg,
5417                 buttons: this.YESNO,
5418                 fn: fn,
5419                 scope : scope,
5420                 modal : true
5421             });
5422             return this;
5423         },
5424
5425         /**
5426          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5428          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429          * (could also be the top-right close button) and the text that was entered will be passed as the two
5430          * parameters to the callback.
5431          * @param {String} title The title bar text
5432          * @param {String} msg The message box body text
5433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434          * @param {Object} scope (optional) The scope of the callback function
5435          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437          * @return {Roo.MessageBox} This message box
5438          */
5439         prompt : function(title, msg, fn, scope, multiline){
5440             this.show({
5441                 title : title,
5442                 msg : msg,
5443                 buttons: this.OKCANCEL,
5444                 fn: fn,
5445                 minWidth:250,
5446                 scope : scope,
5447                 prompt:true,
5448                 multiline: multiline,
5449                 modal : true
5450             });
5451             return this;
5452         },
5453
5454         /**
5455          * Button config that displays a single OK button
5456          * @type Object
5457          */
5458         OK : {ok:true},
5459         /**
5460          * Button config that displays Yes and No buttons
5461          * @type Object
5462          */
5463         YESNO : {yes:true, no:true},
5464         /**
5465          * Button config that displays OK and Cancel buttons
5466          * @type Object
5467          */
5468         OKCANCEL : {ok:true, cancel:true},
5469         /**
5470          * Button config that displays Yes, No and Cancel buttons
5471          * @type Object
5472          */
5473         YESNOCANCEL : {yes:true, no:true, cancel:true},
5474
5475         /**
5476          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5477          * @type Number
5478          */
5479         defaultTextHeight : 75,
5480         /**
5481          * The maximum width in pixels of the message box (defaults to 600)
5482          * @type Number
5483          */
5484         maxWidth : 600,
5485         /**
5486          * The minimum width in pixels of the message box (defaults to 100)
5487          * @type Number
5488          */
5489         minWidth : 100,
5490         /**
5491          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5492          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5493          * @type Number
5494          */
5495         minProgressWidth : 250,
5496         /**
5497          * An object containing the default button text strings that can be overriden for localized language support.
5498          * Supported properties are: ok, cancel, yes and no.
5499          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5500          * @type Object
5501          */
5502         buttonText : {
5503             ok : "OK",
5504             cancel : "Cancel",
5505             yes : "Yes",
5506             no : "No"
5507         }
5508     };
5509 }();
5510
5511 /**
5512  * Shorthand for {@link Roo.MessageBox}
5513  */
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 /*
5517  * - LGPL
5518  *
5519  * navbar
5520  * 
5521  */
5522
5523 /**
5524  * @class Roo.bootstrap.nav.Bar
5525  * @extends Roo.bootstrap.Component
5526  * @abstract
5527  * Bootstrap Navbar class
5528
5529  * @constructor
5530  * Create a new Navbar
5531  * @param {Object} config The config object
5532  */
5533
5534
5535 Roo.bootstrap.nav.Bar = function(config){
5536     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5537     this.addEvents({
5538         // raw events
5539         /**
5540          * @event beforetoggle
5541          * Fire before toggle the menu
5542          * @param {Roo.EventObject} e
5543          */
5544         "beforetoggle" : true
5545     });
5546 };
5547
5548 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5549     
5550     
5551    
5552     // private
5553     navItems : false,
5554     loadMask : false,
5555     
5556     
5557     getAutoCreate : function(){
5558         
5559         
5560         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5561         
5562     },
5563     
5564     initEvents :function ()
5565     {
5566         //Roo.log(this.el.select('.navbar-toggle',true));
5567         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5568         
5569         var mark = {
5570             tag: "div",
5571             cls:"x-dlg-mask"
5572         };
5573         
5574         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5575         
5576         var size = this.el.getSize();
5577         this.maskEl.setSize(size.width, size.height);
5578         this.maskEl.enableDisplayMode("block");
5579         this.maskEl.hide();
5580         
5581         if(this.loadMask){
5582             this.maskEl.show();
5583         }
5584     },
5585     
5586     
5587     getChildContainer : function()
5588     {
5589         if (this.el && this.el.select('.collapse').getCount()) {
5590             return this.el.select('.collapse',true).first();
5591         }
5592         
5593         return this.el;
5594     },
5595     
5596     mask : function()
5597     {
5598         this.maskEl.show();
5599     },
5600     
5601     unmask : function()
5602     {
5603         this.maskEl.hide();
5604     },
5605     onToggle : function()
5606     {
5607         
5608         if(this.fireEvent('beforetoggle', this) === false){
5609             return;
5610         }
5611         var ce = this.el.select('.navbar-collapse',true).first();
5612       
5613         if (!ce.hasClass('show')) {
5614            this.expand();
5615         } else {
5616             this.collapse();
5617         }
5618         
5619         
5620     
5621     },
5622     /**
5623      * Expand the navbar pulldown 
5624      */
5625     expand : function ()
5626     {
5627        
5628         var ce = this.el.select('.navbar-collapse',true).first();
5629         if (ce.hasClass('collapsing')) {
5630             return;
5631         }
5632         ce.dom.style.height = '';
5633                // show it...
5634         ce.addClass('in'); // old...
5635         ce.removeClass('collapse');
5636         ce.addClass('show');
5637         var h = ce.getHeight();
5638         Roo.log(h);
5639         ce.removeClass('show');
5640         // at this point we should be able to see it..
5641         ce.addClass('collapsing');
5642         
5643         ce.setHeight(0); // resize it ...
5644         ce.on('transitionend', function() {
5645             //Roo.log('done transition');
5646             ce.removeClass('collapsing');
5647             ce.addClass('show');
5648             ce.removeClass('collapse');
5649
5650             ce.dom.style.height = '';
5651         }, this, { single: true} );
5652         ce.setHeight(h);
5653         ce.dom.scrollTop = 0;
5654     },
5655     /**
5656      * Collapse the navbar pulldown 
5657      */
5658     collapse : function()
5659     {
5660          var ce = this.el.select('.navbar-collapse',true).first();
5661        
5662         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5663             // it's collapsed or collapsing..
5664             return;
5665         }
5666         ce.removeClass('in'); // old...
5667         ce.setHeight(ce.getHeight());
5668         ce.removeClass('show');
5669         ce.addClass('collapsing');
5670         
5671         ce.on('transitionend', function() {
5672             ce.dom.style.height = '';
5673             ce.removeClass('collapsing');
5674             ce.addClass('collapse');
5675         }, this, { single: true} );
5676         ce.setHeight(0);
5677     }
5678     
5679     
5680     
5681 });
5682
5683
5684
5685  
5686
5687  /*
5688  * - LGPL
5689  *
5690  * navbar
5691  * 
5692  */
5693
5694 /**
5695  * @class Roo.bootstrap.nav.Simplebar
5696  * @extends Roo.bootstrap.nav.Bar
5697  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5698  * Bootstrap Sidebar class
5699  *
5700  * @cfg {Boolean} inverse is inverted color
5701  * 
5702  * @cfg {String} type (nav | pills | tabs)
5703  * @cfg {Boolean} arrangement stacked | justified
5704  * @cfg {String} align (left | right) alignment
5705  * 
5706  * @cfg {Boolean} main (true|false) main nav bar? default false
5707  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5708  * 
5709  * @cfg {String} tag (header|footer|nav|div) default is nav 
5710
5711  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5712  * 
5713  * 
5714  * @constructor
5715  * Create a new Sidebar
5716  * @param {Object} config The config object
5717  */
5718
5719
5720 Roo.bootstrap.nav.Simplebar = function(config){
5721     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5722 };
5723
5724 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5725     
5726     inverse: false,
5727     
5728     type: false,
5729     arrangement: '',
5730     align : false,
5731     
5732     weight : 'light',
5733     
5734     main : false,
5735     
5736     
5737     tag : false,
5738     
5739     
5740     getAutoCreate : function(){
5741         
5742         
5743         var cfg = {
5744             tag : this.tag || 'div',
5745             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5746         };
5747         if (['light','white'].indexOf(this.weight) > -1) {
5748             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5749         }
5750         cfg.cls += ' bg-' + this.weight;
5751         
5752         if (this.inverse) {
5753             cfg.cls += ' navbar-inverse';
5754             
5755         }
5756         
5757         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5758         
5759         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5760             return cfg;
5761         }
5762         
5763         
5764     
5765         
5766         cfg.cn = [
5767             {
5768                 cls: 'nav nav-' + this.xtype,
5769                 tag : 'ul'
5770             }
5771         ];
5772         
5773          
5774         this.type = this.type || 'nav';
5775         if (['tabs','pills'].indexOf(this.type) != -1) {
5776             cfg.cn[0].cls += ' nav-' + this.type
5777         
5778         
5779         } else {
5780             if (this.type!=='nav') {
5781                 Roo.log('nav type must be nav/tabs/pills')
5782             }
5783             cfg.cn[0].cls += ' navbar-nav'
5784         }
5785         
5786         
5787         
5788         
5789         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790             cfg.cn[0].cls += ' nav-' + this.arrangement;
5791         }
5792         
5793         
5794         if (this.align === 'right') {
5795             cfg.cn[0].cls += ' navbar-right';
5796         }
5797         
5798         
5799         
5800         
5801         return cfg;
5802     
5803         
5804     }
5805     
5806     
5807     
5808 });
5809
5810
5811
5812  
5813
5814  
5815        /*
5816  * - LGPL
5817  *
5818  * navbar
5819  * navbar-fixed-top
5820  * navbar-expand-md  fixed-top 
5821  */
5822
5823 /**
5824  * @class Roo.bootstrap.nav.Headerbar
5825  * @extends Roo.bootstrap.nav.Simplebar
5826  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5827  * Bootstrap Sidebar class
5828  *
5829  * @cfg {String} brand what is brand
5830  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5831  * @cfg {String} brand_href href of the brand
5832  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5833  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5834  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5835  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5836  * 
5837  * @constructor
5838  * Create a new Sidebar
5839  * @param {Object} config The config object
5840  */
5841
5842
5843 Roo.bootstrap.nav.Headerbar = function(config){
5844     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5845       
5846 };
5847
5848 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5849     
5850     position: '',
5851     brand: '',
5852     brand_href: false,
5853     srButton : true,
5854     autohide : false,
5855     desktopCenter : false,
5856    
5857     
5858     getAutoCreate : function(){
5859         
5860         var   cfg = {
5861             tag: this.nav || 'nav',
5862             cls: 'navbar navbar-expand-md',
5863             role: 'navigation',
5864             cn: []
5865         };
5866         
5867         var cn = cfg.cn;
5868         if (this.desktopCenter) {
5869             cn.push({cls : 'container', cn : []});
5870             cn = cn[0].cn;
5871         }
5872         
5873         if(this.srButton){
5874             var btn = {
5875                 tag: 'button',
5876                 type: 'button',
5877                 cls: 'navbar-toggle navbar-toggler',
5878                 'data-toggle': 'collapse',
5879                 cn: [
5880                     {
5881                         tag: 'span',
5882                         cls: 'sr-only',
5883                         html: 'Toggle navigation'
5884                     },
5885                     {
5886                         tag: 'span',
5887                         cls: 'icon-bar navbar-toggler-icon'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     }
5897                 ]
5898             };
5899             
5900             cn.push( Roo.bootstrap.version == 4 ? btn : {
5901                 tag: 'div',
5902                 cls: 'navbar-header',
5903                 cn: [
5904                     btn
5905                 ]
5906             });
5907         }
5908         
5909         cn.push({
5910             tag: 'div',
5911             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5912             cn : []
5913         });
5914         
5915         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5916         
5917         if (['light','white'].indexOf(this.weight) > -1) {
5918             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5919         }
5920         cfg.cls += ' bg-' + this.weight;
5921         
5922         
5923         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5924             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5925             
5926             // tag can override this..
5927             
5928             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5929         }
5930         
5931         if (this.brand !== '') {
5932             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5933             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5934                 tag: 'a',
5935                 href: this.brand_href ? this.brand_href : '#',
5936                 cls: 'navbar-brand',
5937                 cn: [
5938                 this.brand
5939                 ]
5940             });
5941         }
5942         
5943         if(this.main){
5944             cfg.cls += ' main-nav';
5945         }
5946         
5947         
5948         return cfg;
5949
5950         
5951     },
5952     getHeaderChildContainer : function()
5953     {
5954         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5955             return this.el.select('.navbar-header',true).first();
5956         }
5957         
5958         return this.getChildContainer();
5959     },
5960     
5961     getChildContainer : function()
5962     {
5963          
5964         return this.el.select('.roo-navbar-collapse',true).first();
5965          
5966         
5967     },
5968     
5969     initEvents : function()
5970     {
5971         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5972         
5973         if (this.autohide) {
5974             
5975             var prevScroll = 0;
5976             var ft = this.el;
5977             
5978             Roo.get(document).on('scroll',function(e) {
5979                 var ns = Roo.get(document).getScroll().top;
5980                 var os = prevScroll;
5981                 prevScroll = ns;
5982                 
5983                 if(ns > os){
5984                     ft.removeClass('slideDown');
5985                     ft.addClass('slideUp');
5986                     return;
5987                 }
5988                 ft.removeClass('slideUp');
5989                 ft.addClass('slideDown');
5990                  
5991               
5992           },this);
5993         }
5994     }    
5995     
5996 });
5997
5998
5999
6000  
6001
6002  /*
6003  * - LGPL
6004  *
6005  * navbar
6006  * 
6007  */
6008
6009 /**
6010  * @class Roo.bootstrap.nav.Sidebar
6011  * @extends Roo.bootstrap.nav.Bar
6012  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6013  * Bootstrap Sidebar class
6014  * 
6015  * @constructor
6016  * Create a new Sidebar
6017  * @param {Object} config The config object
6018  */
6019
6020
6021 Roo.bootstrap.nav.Sidebar = function(config){
6022     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6023 };
6024
6025 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6026     
6027     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6028     
6029     getAutoCreate : function(){
6030         
6031         
6032         return  {
6033             tag: 'div',
6034             cls: 'sidebar sidebar-nav'
6035         };
6036     
6037         
6038     }
6039     
6040     
6041     
6042 });
6043
6044
6045
6046  
6047
6048  /*
6049  * - LGPL
6050  *
6051  * nav group
6052  * 
6053  */
6054
6055 /**
6056  * @class Roo.bootstrap.nav.Group
6057  * @extends Roo.bootstrap.Component
6058  * @children Roo.bootstrap.nav.Item
6059  * Bootstrap NavGroup class
6060  * @cfg {String} align (left|right)
6061  * @cfg {Boolean} inverse
6062  * @cfg {String} type (nav|pills|tab) default nav
6063  * @cfg {String} navId - reference Id for navbar.
6064  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6065  * 
6066  * @constructor
6067  * Create a new nav group
6068  * @param {Object} config The config object
6069  */
6070
6071 Roo.bootstrap.nav.Group = function(config){
6072     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6073     this.navItems = [];
6074    
6075     Roo.bootstrap.nav.Group.register(this);
6076      this.addEvents({
6077         /**
6078              * @event changed
6079              * Fires when the active item changes
6080              * @param {Roo.bootstrap.nav.Group} this
6081              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6083          */
6084         'changed': true
6085      });
6086     
6087 };
6088
6089 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6090     
6091     align: '',
6092     inverse: false,
6093     form: false,
6094     type: 'nav',
6095     navId : '',
6096     // private
6097     pilltype : true,
6098     
6099     navItems : false, 
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag : 'ul',
6107             cls: 'nav' 
6108         };
6109         if (Roo.bootstrap.version == 4) {
6110             if (['tabs','pills'].indexOf(this.type) != -1) {
6111                 cfg.cls += ' nav-' + this.type; 
6112             } else {
6113                 // trying to remove so header bar can right align top?
6114                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115                     // do not use on header bar... 
6116                     cfg.cls += ' navbar-nav';
6117                 }
6118             }
6119             
6120         } else {
6121             if (['tabs','pills'].indexOf(this.type) != -1) {
6122                 cfg.cls += ' nav-' + this.type
6123             } else {
6124                 if (this.type !== 'nav') {
6125                     Roo.log('nav type must be nav/tabs/pills')
6126                 }
6127                 cfg.cls += ' navbar-nav'
6128             }
6129         }
6130         
6131         if (this.parent() && this.parent().sidebar) {
6132             cfg = {
6133                 tag: 'ul',
6134                 cls: 'dashboard-menu sidebar-menu'
6135             };
6136             
6137             return cfg;
6138         }
6139         
6140         if (this.form === true) {
6141             cfg = {
6142                 tag: 'form',
6143                 cls: 'navbar-form form-inline'
6144             };
6145             //nav navbar-right ml-md-auto
6146             if (this.align === 'right') {
6147                 cfg.cls += ' navbar-right ml-md-auto';
6148             } else {
6149                 cfg.cls += ' navbar-left';
6150             }
6151         }
6152         
6153         if (this.align === 'right') {
6154             cfg.cls += ' navbar-right ml-md-auto';
6155         } else {
6156             cfg.cls += ' mr-auto';
6157         }
6158         
6159         if (this.inverse) {
6160             cfg.cls += ' navbar-inverse';
6161             
6162         }
6163         
6164         
6165         return cfg;
6166     },
6167     /**
6168     * sets the active Navigation item
6169     * @param {Roo.bootstrap.nav.Item} the new current navitem
6170     */
6171     setActiveItem : function(item)
6172     {
6173         var prev = false;
6174         Roo.each(this.navItems, function(v){
6175             if (v == item) {
6176                 return ;
6177             }
6178             if (v.isActive()) {
6179                 v.setActive(false, true);
6180                 prev = v;
6181                 
6182             }
6183             
6184         });
6185
6186         item.setActive(true, true);
6187         this.fireEvent('changed', this, item, prev);
6188         
6189         
6190     },
6191     /**
6192     * gets the active Navigation item
6193     * @return {Roo.bootstrap.nav.Item} the current navitem
6194     */
6195     getActive : function()
6196     {
6197         
6198         var prev = false;
6199         Roo.each(this.navItems, function(v){
6200             
6201             if (v.isActive()) {
6202                 prev = v;
6203                 
6204             }
6205             
6206         });
6207         return prev;
6208     },
6209     
6210     indexOfNav : function()
6211     {
6212         
6213         var prev = false;
6214         Roo.each(this.navItems, function(v,i){
6215             
6216             if (v.isActive()) {
6217                 prev = i;
6218                 
6219             }
6220             
6221         });
6222         return prev;
6223     },
6224     /**
6225     * adds a Navigation item
6226     * @param {Roo.bootstrap.nav.Item} the navitem to add
6227     */
6228     addItem : function(cfg)
6229     {
6230         if (this.form && Roo.bootstrap.version == 4) {
6231             cfg.tag = 'div';
6232         }
6233         var cn = new Roo.bootstrap.nav.Item(cfg);
6234         this.register(cn);
6235         cn.parentId = this.id;
6236         cn.onRender(this.el, null);
6237         return cn;
6238     },
6239     /**
6240     * register a Navigation item
6241     * @param {Roo.bootstrap.nav.Item} the navitem to add
6242     */
6243     register : function(item)
6244     {
6245         this.navItems.push( item);
6246         item.navId = this.navId;
6247     
6248     },
6249     
6250     /**
6251     * clear all the Navigation item
6252     */
6253    
6254     clearAll : function()
6255     {
6256         this.navItems = [];
6257         this.el.dom.innerHTML = '';
6258     },
6259     
6260     getNavItem: function(tabId)
6261     {
6262         var ret = false;
6263         Roo.each(this.navItems, function(e) {
6264             if (e.tabId == tabId) {
6265                ret =  e;
6266                return false;
6267             }
6268             return true;
6269             
6270         });
6271         return ret;
6272     },
6273     
6274     setActiveNext : function()
6275     {
6276         var i = this.indexOfNav(this.getActive());
6277         if (i > this.navItems.length) {
6278             return;
6279         }
6280         this.setActiveItem(this.navItems[i+1]);
6281     },
6282     setActivePrev : function()
6283     {
6284         var i = this.indexOfNav(this.getActive());
6285         if (i  < 1) {
6286             return;
6287         }
6288         this.setActiveItem(this.navItems[i-1]);
6289     },
6290     clearWasActive : function(except) {
6291         Roo.each(this.navItems, function(e) {
6292             if (e.tabId != except.tabId && e.was_active) {
6293                e.was_active = false;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299     },
6300     getWasActive : function ()
6301     {
6302         var r = false;
6303         Roo.each(this.navItems, function(e) {
6304             if (e.was_active) {
6305                r = e;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311         return r;
6312     }
6313     
6314     
6315 });
6316
6317  
6318 Roo.apply(Roo.bootstrap.nav.Group, {
6319     
6320     groups: {},
6321      /**
6322     * register a Navigation Group
6323     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6324     */
6325     register : function(navgrp)
6326     {
6327         this.groups[navgrp.navId] = navgrp;
6328         
6329     },
6330     /**
6331     * fetch a Navigation Group based on the navigation ID
6332     * @param {string} the navgroup to add
6333     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6334     */
6335     get: function(navId) {
6336         if (typeof(this.groups[navId]) == 'undefined') {
6337             return false;
6338             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6339         }
6340         return this.groups[navId] ;
6341     }
6342     
6343     
6344     
6345 });
6346
6347  /**
6348  * @class Roo.bootstrap.nav.Item
6349  * @extends Roo.bootstrap.Component
6350  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351  * @parent Roo.bootstrap.nav.Group
6352  * @licence LGPL
6353  * Bootstrap Navbar.NavItem class
6354  * 
6355  * @cfg {String} href  link to
6356  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357  * @cfg {Boolean} button_outline show and outlined button
6358  * @cfg {String} html content of button
6359  * @cfg {String} badge text inside badge
6360  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361  * @cfg {String} glyphicon DEPRICATED - use fa
6362  * @cfg {String} icon DEPRICATED - use fa
6363  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364  * @cfg {Boolean} active Is item active
6365  * @cfg {Boolean} disabled Is item disabled
6366  * @cfg {String} linkcls  Link Class
6367  * @cfg {Boolean} preventDefault (true | false) default false
6368  * @cfg {String} tabId the tab that this item activates.
6369  * @cfg {String} tagtype (a|span) render as a href or span?
6370  * @cfg {Boolean} animateRef (true|false) link to element default false  
6371  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6372   
6373  * @constructor
6374  * Create a new Navbar Item
6375  * @param {Object} config The config object
6376  */
6377 Roo.bootstrap.nav.Item = function(config){
6378     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6379     this.addEvents({
6380         // raw events
6381         /**
6382          * @event click
6383          * The raw click event for the entire grid.
6384          * @param {Roo.EventObject} e
6385          */
6386         "click" : true,
6387          /**
6388             * @event changed
6389             * Fires when the active item active state changes
6390             * @param {Roo.bootstrap.nav.Item} this
6391             * @param {boolean} state the new state
6392              
6393          */
6394         'changed': true,
6395         /**
6396             * @event scrollto
6397             * Fires when scroll to element
6398             * @param {Roo.bootstrap.nav.Item} this
6399             * @param {Object} options
6400             * @param {Roo.EventObject} e
6401              
6402          */
6403         'scrollto': true
6404     });
6405    
6406 };
6407
6408 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6409     
6410     href: false,
6411     html: '',
6412     badge: '',
6413     icon: false,
6414     fa : false,
6415     glyphicon: false,
6416     active: false,
6417     preventDefault : false,
6418     tabId : false,
6419     tagtype : 'a',
6420     tag: 'li',
6421     disabled : false,
6422     animateRef : false,
6423     was_active : false,
6424     button_weight : '',
6425     button_outline : false,
6426     linkcls : '',
6427     navLink: false,
6428     
6429     getAutoCreate : function(){
6430          
6431         var cfg = {
6432             tag: this.tag,
6433             cls: 'nav-item'
6434         };
6435         
6436         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6437         
6438         if (this.active) {
6439             cfg.cls +=  ' active' ;
6440         }
6441         if (this.disabled) {
6442             cfg.cls += ' disabled';
6443         }
6444         
6445         // BS4 only?
6446         if (this.button_weight.length) {
6447             cfg.tag = this.href ? 'a' : 'button';
6448             cfg.html = this.html || '';
6449             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6450             if (this.href) {
6451                 cfg.href = this.href;
6452             }
6453             if (this.fa) {
6454                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6455             } else {
6456                 cfg.cls += " nav-html";
6457             }
6458             
6459             // menu .. should add dropdown-menu class - so no need for carat..
6460             
6461             if (this.badge !== '') {
6462                  
6463                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6464             }
6465             return cfg;
6466         }
6467         
6468         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6469             cfg.cn = [
6470                 {
6471                     tag: this.tagtype,
6472                     href : this.href || "#",
6473                     html: this.html || '',
6474                     cls : ''
6475                 }
6476             ];
6477             if (this.tagtype == 'a') {
6478                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6479         
6480             }
6481             if (this.icon) {
6482                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483             } else  if (this.fa) {
6484                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else if(this.glyphicon) {
6486                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6487             } else {
6488                 cfg.cn[0].cls += " nav-html";
6489             }
6490             
6491             if (this.menu) {
6492                 cfg.cn[0].html += " <span class='caret'></span>";
6493              
6494             }
6495             
6496             if (this.badge !== '') {
6497                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6498             }
6499         }
6500         
6501         
6502         
6503         return cfg;
6504     },
6505     onRender : function(ct, position)
6506     {
6507        // Roo.log("Call onRender: " + this.xtype);
6508         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6509             this.tag = 'div';
6510         }
6511         
6512         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6513         this.navLink = this.el.select('.nav-link',true).first();
6514         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6515         return ret;
6516     },
6517       
6518     
6519     initEvents: function() 
6520     {
6521         if (typeof (this.menu) != 'undefined') {
6522             this.menu.parentType = this.xtype;
6523             this.menu.triggerEl = this.el;
6524             this.menu = this.addxtype(Roo.apply({}, this.menu));
6525         }
6526         
6527         this.el.on('click', this.onClick, this);
6528         
6529         //if(this.tagtype == 'span'){
6530         //    this.el.select('span',true).on('click', this.onClick, this);
6531         //}
6532        
6533         // at this point parent should be available..
6534         this.parent().register(this);
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if (e.getTarget('.dropdown-menu-item')) {
6540             // did you click on a menu itemm.... - then don't trigger onclick..
6541             return;
6542         }
6543         
6544         if(
6545                 this.preventDefault || 
6546                 this.href == '#' 
6547         ){
6548             Roo.log("NavItem - prevent Default?");
6549             e.preventDefault();
6550         }
6551         
6552         if (this.disabled) {
6553             return;
6554         }
6555         
6556         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6557         if (tg && tg.transition) {
6558             Roo.log("waiting for the transitionend");
6559             return;
6560         }
6561         
6562         
6563         
6564         //Roo.log("fire event clicked");
6565         if(this.fireEvent('click', this, e) === false){
6566             return;
6567         };
6568         
6569         if(this.tagtype == 'span'){
6570             return;
6571         }
6572         
6573         //Roo.log(this.href);
6574         var ael = this.el.select('a',true).first();
6575         //Roo.log(ael);
6576         
6577         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6578             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6579             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6580                 return; // ignore... - it's a 'hash' to another page.
6581             }
6582             Roo.log("NavItem - prevent Default?");
6583             e.preventDefault();
6584             this.scrollToElement(e);
6585         }
6586         
6587         
6588         var p =  this.parent();
6589    
6590         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6591             if (typeof(p.setActiveItem) !== 'undefined') {
6592                 p.setActiveItem(this);
6593             }
6594         }
6595         
6596         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6597         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6598             // remove the collapsed menu expand...
6599             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6600         }
6601     },
6602     
6603     isActive: function () {
6604         return this.active
6605     },
6606     setActive : function(state, fire, is_was_active)
6607     {
6608         if (this.active && !state && this.navId) {
6609             this.was_active = true;
6610             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6611             if (nv) {
6612                 nv.clearWasActive(this);
6613             }
6614             
6615         }
6616         this.active = state;
6617         
6618         if (!state ) {
6619             this.el.removeClass('active');
6620             this.navLink ? this.navLink.removeClass('active') : false;
6621         } else if (!this.el.hasClass('active')) {
6622             
6623             this.el.addClass('active');
6624             if (Roo.bootstrap.version == 4 && this.navLink ) {
6625                 this.navLink.addClass('active');
6626             }
6627             
6628         }
6629         if (fire) {
6630             this.fireEvent('changed', this, state);
6631         }
6632         
6633         // show a panel if it's registered and related..
6634         
6635         if (!this.navId || !this.tabId || !state || is_was_active) {
6636             return;
6637         }
6638         
6639         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6640         if (!tg) {
6641             return;
6642         }
6643         var pan = tg.getPanelByName(this.tabId);
6644         if (!pan) {
6645             return;
6646         }
6647         // if we can not flip to new panel - go back to old nav highlight..
6648         if (false == tg.showPanel(pan)) {
6649             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6650             if (nv) {
6651                 var onav = nv.getWasActive();
6652                 if (onav) {
6653                     onav.setActive(true, false, true);
6654                 }
6655             }
6656             
6657         }
6658         
6659         
6660         
6661     },
6662      // this should not be here...
6663     setDisabled : function(state)
6664     {
6665         this.disabled = state;
6666         if (!state ) {
6667             this.el.removeClass('disabled');
6668         } else if (!this.el.hasClass('disabled')) {
6669             this.el.addClass('disabled');
6670         }
6671         
6672     },
6673     
6674     /**
6675      * Fetch the element to display the tooltip on.
6676      * @return {Roo.Element} defaults to this.el
6677      */
6678     tooltipEl : function()
6679     {
6680         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6681     },
6682     
6683     scrollToElement : function(e)
6684     {
6685         var c = document.body;
6686         
6687         /*
6688          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6689          */
6690         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6691             c = document.documentElement;
6692         }
6693         
6694         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6695         
6696         if(!target){
6697             return;
6698         }
6699
6700         var o = target.calcOffsetsTo(c);
6701         
6702         var options = {
6703             target : target,
6704             value : o[1]
6705         };
6706         
6707         this.fireEvent('scrollto', this, options, e);
6708         
6709         Roo.get(c).scrollTo('top', options.value, true);
6710         
6711         return;
6712     },
6713     /**
6714      * Set the HTML (text content) of the item
6715      * @param {string} html  content for the nav item
6716      */
6717     setHtml : function(html)
6718     {
6719         this.html = html;
6720         this.htmlEl.dom.innerHTML = html;
6721         
6722     } 
6723 });
6724  
6725
6726  /*
6727  * - LGPL
6728  *
6729  * sidebar item
6730  *
6731  *  li
6732  *    <span> icon </span>
6733  *    <span> text </span>
6734  *    <span>badge </span>
6735  */
6736
6737 /**
6738  * @class Roo.bootstrap.nav.SidebarItem
6739  * @extends Roo.bootstrap.nav.Item
6740  * Bootstrap Navbar.NavSidebarItem class
6741  * 
6742  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6743  * {Boolean} open is the menu open
6744  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6745  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6746  * {String} buttonSize (sm|md|lg)the extra classes for the button
6747  * {Boolean} showArrow show arrow next to the text (default true)
6748  * @constructor
6749  * Create a new Navbar Button
6750  * @param {Object} config The config object
6751  */
6752 Roo.bootstrap.nav.SidebarItem = function(config){
6753     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6754     this.addEvents({
6755         // raw events
6756         /**
6757          * @event click
6758          * The raw click event for the entire grid.
6759          * @param {Roo.EventObject} e
6760          */
6761         "click" : true,
6762          /**
6763             * @event changed
6764             * Fires when the active item active state changes
6765             * @param {Roo.bootstrap.nav.SidebarItem} this
6766             * @param {boolean} state the new state
6767              
6768          */
6769         'changed': true
6770     });
6771    
6772 };
6773
6774 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6775     
6776     badgeWeight : 'default',
6777     
6778     open: false,
6779     
6780     buttonView : false,
6781     
6782     buttonWeight : 'default',
6783     
6784     buttonSize : 'md',
6785     
6786     showArrow : true,
6787     
6788     getAutoCreate : function(){
6789         
6790         
6791         var a = {
6792                 tag: 'a',
6793                 href : this.href || '#',
6794                 cls: '',
6795                 html : '',
6796                 cn : []
6797         };
6798         
6799         if(this.buttonView){
6800             a = {
6801                 tag: 'button',
6802                 href : this.href || '#',
6803                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804                 html : this.html,
6805                 cn : []
6806             };
6807         }
6808         
6809         var cfg = {
6810             tag: 'li',
6811             cls: '',
6812             cn: [ a ]
6813         };
6814         
6815         if (this.active) {
6816             cfg.cls += ' active';
6817         }
6818         
6819         if (this.disabled) {
6820             cfg.cls += ' disabled';
6821         }
6822         if (this.open) {
6823             cfg.cls += ' open x-open';
6824         }
6825         // left icon..
6826         if (this.glyphicon || this.icon) {
6827             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6828             a.cn.push({ tag : 'i', cls : c }) ;
6829         }
6830         
6831         if(!this.buttonView){
6832             var span = {
6833                 tag: 'span',
6834                 html : this.html || ''
6835             };
6836
6837             a.cn.push(span);
6838             
6839         }
6840         
6841         if (this.badge !== '') {
6842             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6843         }
6844         
6845         if (this.menu) {
6846             
6847             if(this.showArrow){
6848                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6849             }
6850             
6851             a.cls += ' dropdown-toggle treeview' ;
6852         }
6853         
6854         return cfg;
6855     },
6856     
6857     initEvents : function()
6858     { 
6859         if (typeof (this.menu) != 'undefined') {
6860             this.menu.parentType = this.xtype;
6861             this.menu.triggerEl = this.el;
6862             this.menu = this.addxtype(Roo.apply({}, this.menu));
6863         }
6864         
6865         this.el.on('click', this.onClick, this);
6866         
6867         if(this.badge !== ''){
6868             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6869         }
6870         
6871     },
6872     
6873     onClick : function(e)
6874     {
6875         if(this.disabled){
6876             e.preventDefault();
6877             return;
6878         }
6879         
6880         if(this.preventDefault){
6881             e.preventDefault();
6882         }
6883         
6884         this.fireEvent('click', this, e);
6885     },
6886     
6887     disable : function()
6888     {
6889         this.setDisabled(true);
6890     },
6891     
6892     enable : function()
6893     {
6894         this.setDisabled(false);
6895     },
6896     
6897     setDisabled : function(state)
6898     {
6899         if(this.disabled == state){
6900             return;
6901         }
6902         
6903         this.disabled = state;
6904         
6905         if (state) {
6906             this.el.addClass('disabled');
6907             return;
6908         }
6909         
6910         this.el.removeClass('disabled');
6911         
6912         return;
6913     },
6914     
6915     setActive : function(state)
6916     {
6917         if(this.active == state){
6918             return;
6919         }
6920         
6921         this.active = state;
6922         
6923         if (state) {
6924             this.el.addClass('active');
6925             return;
6926         }
6927         
6928         this.el.removeClass('active');
6929         
6930         return;
6931     },
6932     
6933     isActive: function () 
6934     {
6935         return this.active;
6936     },
6937     
6938     setBadge : function(str)
6939     {
6940         if(!this.badgeEl){
6941             return;
6942         }
6943         
6944         this.badgeEl.dom.innerHTML = str;
6945     }
6946     
6947    
6948      
6949  
6950 });
6951  
6952
6953  /*
6954  * - LGPL
6955  *
6956  * nav progress bar
6957  * 
6958  */
6959
6960 /**
6961  * @class Roo.bootstrap.nav.ProgressBar
6962  * @extends Roo.bootstrap.Component
6963  * @children Roo.bootstrap.nav.ProgressBarItem
6964  * Bootstrap NavProgressBar class
6965  * 
6966  * @constructor
6967  * Create a new nav progress bar - a bar indicating step along a process
6968  * @param {Object} config The config object
6969  */
6970
6971 Roo.bootstrap.nav.ProgressBar = function(config){
6972     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6973
6974     this.bullets = this.bullets || [];
6975    
6976 //    Roo.bootstrap.nav.ProgressBar.register(this);
6977      this.addEvents({
6978         /**
6979              * @event changed
6980              * Fires when the active item changes
6981              * @param {Roo.bootstrap.nav.ProgressBar} this
6982              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6983              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6984          */
6985         'changed': true
6986      });
6987     
6988 };
6989
6990 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6991     /**
6992      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6993      * Bullets for the Nav Progress bar for the toolbar
6994      */
6995     bullets : [],
6996     barItems : [],
6997     
6998     getAutoCreate : function()
6999     {
7000         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7001         
7002         cfg = {
7003             tag : 'div',
7004             cls : 'roo-navigation-bar-group',
7005             cn : [
7006                 {
7007                     tag : 'div',
7008                     cls : 'roo-navigation-top-bar'
7009                 },
7010                 {
7011                     tag : 'div',
7012                     cls : 'roo-navigation-bullets-bar',
7013                     cn : [
7014                         {
7015                             tag : 'ul',
7016                             cls : 'roo-navigation-bar'
7017                         }
7018                     ]
7019                 },
7020                 
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bottom-bar'
7024                 }
7025             ]
7026             
7027         };
7028         
7029         return cfg;
7030         
7031     },
7032     
7033     initEvents: function() 
7034     {
7035         
7036     },
7037     
7038     onRender : function(ct, position) 
7039     {
7040         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7041         
7042         if(this.bullets.length){
7043             Roo.each(this.bullets, function(b){
7044                this.addItem(b);
7045             }, this);
7046         }
7047         
7048         this.format();
7049         
7050     },
7051     
7052     addItem : function(cfg)
7053     {
7054         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7055         
7056         item.parentId = this.id;
7057         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7058         
7059         if(cfg.html){
7060             var top = new Roo.bootstrap.Element({
7061                 tag : 'div',
7062                 cls : 'roo-navigation-bar-text'
7063             });
7064             
7065             var bottom = new Roo.bootstrap.Element({
7066                 tag : 'div',
7067                 cls : 'roo-navigation-bar-text'
7068             });
7069             
7070             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7071             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7072             
7073             var topText = new Roo.bootstrap.Element({
7074                 tag : 'span',
7075                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7076             });
7077             
7078             var bottomText = new Roo.bootstrap.Element({
7079                 tag : 'span',
7080                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7081             });
7082             
7083             topText.onRender(top.el, null);
7084             bottomText.onRender(bottom.el, null);
7085             
7086             item.topEl = top;
7087             item.bottomEl = bottom;
7088         }
7089         
7090         this.barItems.push(item);
7091         
7092         return item;
7093     },
7094     
7095     getActive : function()
7096     {
7097         var active = false;
7098         
7099         Roo.each(this.barItems, function(v){
7100             
7101             if (!v.isActive()) {
7102                 return;
7103             }
7104             
7105             active = v;
7106             return false;
7107             
7108         });
7109         
7110         return active;
7111     },
7112     
7113     setActiveItem : function(item)
7114     {
7115         var prev = false;
7116         
7117         Roo.each(this.barItems, function(v){
7118             if (v.rid == item.rid) {
7119                 return ;
7120             }
7121             
7122             if (v.isActive()) {
7123                 v.setActive(false);
7124                 prev = v;
7125             }
7126         });
7127
7128         item.setActive(true);
7129         
7130         this.fireEvent('changed', this, item, prev);
7131     },
7132     
7133     getBarItem: function(rid)
7134     {
7135         var ret = false;
7136         
7137         Roo.each(this.barItems, function(e) {
7138             if (e.rid != rid) {
7139                 return;
7140             }
7141             
7142             ret =  e;
7143             return false;
7144         });
7145         
7146         return ret;
7147     },
7148     
7149     indexOfItem : function(item)
7150     {
7151         var index = false;
7152         
7153         Roo.each(this.barItems, function(v, i){
7154             
7155             if (v.rid != item.rid) {
7156                 return;
7157             }
7158             
7159             index = i;
7160             return false
7161         });
7162         
7163         return index;
7164     },
7165     
7166     setActiveNext : function()
7167     {
7168         var i = this.indexOfItem(this.getActive());
7169         
7170         if (i > this.barItems.length) {
7171             return;
7172         }
7173         
7174         this.setActiveItem(this.barItems[i+1]);
7175     },
7176     
7177     setActivePrev : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i  < 1) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i-1]);
7186     },
7187     
7188     format : function()
7189     {
7190         if(!this.barItems.length){
7191             return;
7192         }
7193      
7194         var width = 100 / this.barItems.length;
7195         
7196         Roo.each(this.barItems, function(i){
7197             i.el.setStyle('width', width + '%');
7198             i.topEl.el.setStyle('width', width + '%');
7199             i.bottomEl.el.setStyle('width', width + '%');
7200         }, this);
7201         
7202     }
7203     
7204 });
7205 /*
7206  * - LGPL
7207  *
7208  * Nav Progress Item
7209  * 
7210  */
7211
7212 /**
7213  * @class Roo.bootstrap.nav.ProgressBarItem
7214  * @extends Roo.bootstrap.Component
7215  * Bootstrap NavProgressBarItem class
7216  * @cfg {String} rid the reference id
7217  * @cfg {Boolean} active (true|false) Is item active default false
7218  * @cfg {Boolean} disabled (true|false) Is item active default false
7219  * @cfg {String} html
7220  * @cfg {String} position (top|bottom) text position default bottom
7221  * @cfg {String} icon show icon instead of number
7222  * 
7223  * @constructor
7224  * Create a new NavProgressBarItem
7225  * @param {Object} config The config object
7226  */
7227 Roo.bootstrap.nav.ProgressBarItem = function(config){
7228     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7229     this.addEvents({
7230         // raw events
7231         /**
7232          * @event click
7233          * The raw click event for the entire grid.
7234          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7235          * @param {Roo.EventObject} e
7236          */
7237         "click" : true
7238     });
7239    
7240 };
7241
7242 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7243     
7244     rid : '',
7245     active : false,
7246     disabled : false,
7247     html : '',
7248     position : 'bottom',
7249     icon : false,
7250     
7251     getAutoCreate : function()
7252     {
7253         var iconCls = 'roo-navigation-bar-item-icon';
7254         
7255         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7256         
7257         var cfg = {
7258             tag: 'li',
7259             cls: 'roo-navigation-bar-item',
7260             cn : [
7261                 {
7262                     tag : 'i',
7263                     cls : iconCls
7264                 }
7265             ]
7266         };
7267         
7268         if(this.active){
7269             cfg.cls += ' active';
7270         }
7271         if(this.disabled){
7272             cfg.cls += ' disabled';
7273         }
7274         
7275         return cfg;
7276     },
7277     
7278     disable : function()
7279     {
7280         this.setDisabled(true);
7281     },
7282     
7283     enable : function()
7284     {
7285         this.setDisabled(false);
7286     },
7287     
7288     initEvents: function() 
7289     {
7290         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7291         
7292         this.iconEl.on('click', this.onClick, this);
7293     },
7294     
7295     onClick : function(e)
7296     {
7297         e.preventDefault();
7298         
7299         if(this.disabled){
7300             return;
7301         }
7302         
7303         if(this.fireEvent('click', this, e) === false){
7304             return;
7305         };
7306         
7307         this.parent().setActiveItem(this);
7308     },
7309     
7310     isActive: function () 
7311     {
7312         return this.active;
7313     },
7314     
7315     setActive : function(state)
7316     {
7317         if(this.active == state){
7318             return;
7319         }
7320         
7321         this.active = state;
7322         
7323         if (state) {
7324             this.el.addClass('active');
7325             return;
7326         }
7327         
7328         this.el.removeClass('active');
7329         
7330         return;
7331     },
7332     
7333     setDisabled : function(state)
7334     {
7335         if(this.disabled == state){
7336             return;
7337         }
7338         
7339         this.disabled = state;
7340         
7341         if (state) {
7342             this.el.addClass('disabled');
7343             return;
7344         }
7345         
7346         this.el.removeClass('disabled');
7347     },
7348     
7349     tooltipEl : function()
7350     {
7351         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7352     }
7353 });
7354  
7355
7356  /*
7357  * - LGPL
7358  *
7359  *  Breadcrumb Nav
7360  * 
7361  */
7362 Roo.namespace('Roo.bootstrap.breadcrumb');
7363
7364
7365 /**
7366  * @class Roo.bootstrap.breadcrumb.Nav
7367  * @extends Roo.bootstrap.Component
7368  * Bootstrap Breadcrumb Nav Class
7369  *  
7370  * @children Roo.bootstrap.breadcrumb.Item
7371  * 
7372  * @constructor
7373  * Create a new breadcrumb.Nav
7374  * @param {Object} config The config object
7375  */
7376
7377
7378 Roo.bootstrap.breadcrumb.Nav = function(config){
7379     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7380     
7381     
7382 };
7383
7384 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7385     
7386     getAutoCreate : function()
7387     {
7388
7389         var cfg = {
7390             tag: 'nav',
7391             cn : [
7392                 {
7393                     tag : 'ol',
7394                     cls : 'breadcrumb'
7395                 }
7396             ]
7397             
7398         };
7399           
7400         return cfg;
7401     },
7402     
7403     initEvents: function()
7404     {
7405         this.olEl = this.el.select('ol',true).first();    
7406     },
7407     getChildContainer : function()
7408     {
7409         return this.olEl;  
7410     }
7411     
7412 });
7413
7414  /*
7415  * - LGPL
7416  *
7417  *  Breadcrumb Item
7418  * 
7419  */
7420
7421
7422 /**
7423  * @class Roo.bootstrap.breadcrumb.Nav
7424  * @extends Roo.bootstrap.Component
7425  * @children Roo.bootstrap.Component
7426  * @parent Roo.bootstrap.breadcrumb.Nav
7427  * Bootstrap Breadcrumb Nav Class
7428  *  
7429  * 
7430  * @cfg {String} html the content of the link.
7431  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7432  * @cfg {Boolean} active is it active
7433
7434  * 
7435  * @constructor
7436  * Create a new breadcrumb.Nav
7437  * @param {Object} config The config object
7438  */
7439
7440 Roo.bootstrap.breadcrumb.Item = function(config){
7441     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7442     this.addEvents({
7443         // img events
7444         /**
7445          * @event click
7446          * The img click event for the img.
7447          * @param {Roo.EventObject} e
7448          */
7449         "click" : true
7450     });
7451     
7452 };
7453
7454 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7455     
7456     href: false,
7457     html : '',
7458     
7459     getAutoCreate : function()
7460     {
7461
7462         var cfg = {
7463             tag: 'li',
7464             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7465         };
7466         if (this.href !== false) {
7467             cfg.cn = [{
7468                 tag : 'a',
7469                 href : this.href,
7470                 html : this.html
7471             }];
7472         } else {
7473             cfg.html = this.html;
7474         }
7475         
7476         return cfg;
7477     },
7478     
7479     initEvents: function()
7480     {
7481         if (this.href) {
7482             this.el.select('a', true).first().on('click',this.onClick, this)
7483         }
7484         
7485     },
7486     onClick : function(e)
7487     {
7488         e.preventDefault();
7489         this.fireEvent('click',this,  e);
7490     }
7491     
7492 });
7493
7494  /*
7495  * - LGPL
7496  *
7497  * row
7498  * 
7499  */
7500
7501 /**
7502  * @class Roo.bootstrap.Row
7503  * @extends Roo.bootstrap.Component
7504  * @children Roo.bootstrap.Component
7505  * Bootstrap Row class (contains columns...)
7506  * 
7507  * @constructor
7508  * Create a new Row
7509  * @param {Object} config The config object
7510  */
7511
7512 Roo.bootstrap.Row = function(config){
7513     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7514 };
7515
7516 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7517     
7518     getAutoCreate : function(){
7519        return {
7520             cls: 'row clearfix'
7521        };
7522     }
7523     
7524     
7525 });
7526
7527  
7528
7529  /*
7530  * - LGPL
7531  *
7532  * pagination
7533  * 
7534  */
7535
7536 /**
7537  * @class Roo.bootstrap.Pagination
7538  * @extends Roo.bootstrap.Component
7539  * @children Roo.bootstrap.Pagination
7540  * Bootstrap Pagination class
7541  * 
7542  * @cfg {String} size (xs|sm|md|lg|xl)
7543  * @cfg {Boolean} inverse 
7544  * 
7545  * @constructor
7546  * Create a new Pagination
7547  * @param {Object} config The config object
7548  */
7549
7550 Roo.bootstrap.Pagination = function(config){
7551     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7552 };
7553
7554 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7555     
7556     cls: false,
7557     size: false,
7558     inverse: false,
7559     
7560     getAutoCreate : function(){
7561         var cfg = {
7562             tag: 'ul',
7563                 cls: 'pagination'
7564         };
7565         if (this.inverse) {
7566             cfg.cls += ' inverse';
7567         }
7568         if (this.html) {
7569             cfg.html=this.html;
7570         }
7571         if (this.cls) {
7572             cfg.cls += " " + this.cls;
7573         }
7574         return cfg;
7575     }
7576    
7577 });
7578
7579  
7580
7581  /*
7582  * - LGPL
7583  *
7584  * Pagination item
7585  * 
7586  */
7587
7588
7589 /**
7590  * @class Roo.bootstrap.PaginationItem
7591  * @extends Roo.bootstrap.Component
7592  * Bootstrap PaginationItem class
7593  * @cfg {String} html text
7594  * @cfg {String} href the link
7595  * @cfg {Boolean} preventDefault (true | false) default true
7596  * @cfg {Boolean} active (true | false) default false
7597  * @cfg {Boolean} disabled default false
7598  * 
7599  * 
7600  * @constructor
7601  * Create a new PaginationItem
7602  * @param {Object} config The config object
7603  */
7604
7605
7606 Roo.bootstrap.PaginationItem = function(config){
7607     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7608     this.addEvents({
7609         // raw events
7610         /**
7611          * @event click
7612          * The raw click event for the entire grid.
7613          * @param {Roo.EventObject} e
7614          */
7615         "click" : true
7616     });
7617 };
7618
7619 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7620     
7621     href : false,
7622     html : false,
7623     preventDefault: true,
7624     active : false,
7625     cls : false,
7626     disabled: false,
7627     
7628     getAutoCreate : function(){
7629         var cfg= {
7630             tag: 'li',
7631             cn: [
7632                 {
7633                     tag : 'a',
7634                     href : this.href ? this.href : '#',
7635                     html : this.html ? this.html : ''
7636                 }
7637             ]
7638         };
7639         
7640         if(this.cls){
7641             cfg.cls = this.cls;
7642         }
7643         
7644         if(this.disabled){
7645             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7646         }
7647         
7648         if(this.active){
7649             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7650         }
7651         
7652         return cfg;
7653     },
7654     
7655     initEvents: function() {
7656         
7657         this.el.on('click', this.onClick, this);
7658         
7659     },
7660     onClick : function(e)
7661     {
7662         Roo.log('PaginationItem on click ');
7663         if(this.preventDefault){
7664             e.preventDefault();
7665         }
7666         
7667         if(this.disabled){
7668             return;
7669         }
7670         
7671         this.fireEvent('click', this, e);
7672     }
7673    
7674 });
7675
7676  
7677
7678  /*
7679  * - LGPL
7680  *
7681  * slider
7682  * 
7683  */
7684
7685
7686 /**
7687  * @class Roo.bootstrap.Slider
7688  * @extends Roo.bootstrap.Component
7689  * Bootstrap Slider class
7690  *    
7691  * @constructor
7692  * Create a new Slider
7693  * @param {Object} config The config object
7694  */
7695
7696 Roo.bootstrap.Slider = function(config){
7697     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7698 };
7699
7700 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7701     
7702     getAutoCreate : function(){
7703         
7704         var cfg = {
7705             tag: 'div',
7706             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7707             cn: [
7708                 {
7709                     tag: 'a',
7710                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7711                 }
7712             ]
7713         };
7714         
7715         return cfg;
7716     }
7717    
7718 });
7719
7720  /*
7721  * Based on:
7722  * Ext JS Library 1.1.1
7723  * Copyright(c) 2006-2007, Ext JS, LLC.
7724  *
7725  * Originally Released Under LGPL - original licence link has changed is not relivant.
7726  *
7727  * Fork - LGPL
7728  * <script type="text/javascript">
7729  */
7730  /**
7731  * @extends Roo.dd.DDProxy
7732  * @class Roo.grid.SplitDragZone
7733  * Support for Column Header resizing
7734  * @constructor
7735  * @param {Object} config
7736  */
7737 // private
7738 // This is a support class used internally by the Grid components
7739 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7740     this.grid = grid;
7741     this.view = grid.getView();
7742     this.proxy = this.view.resizeProxy;
7743     Roo.grid.SplitDragZone.superclass.constructor.call(
7744         this,
7745         hd, // ID
7746         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7747         {  // CONFIG
7748             dragElId : Roo.id(this.proxy.dom),
7749             resizeFrame:false
7750         }
7751     );
7752     
7753     this.setHandleElId(Roo.id(hd));
7754     if (hd2 !== false) {
7755         this.setOuterHandleElId(Roo.id(hd2));
7756     }
7757     
7758     this.scroll = false;
7759 };
7760 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7761     fly: Roo.Element.fly,
7762
7763     b4StartDrag : function(x, y){
7764         this.view.headersDisabled = true;
7765         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7766                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7767         );
7768         this.proxy.setHeight(h);
7769         
7770         // for old system colWidth really stored the actual width?
7771         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7772         // which in reality did not work.. - it worked only for fixed sizes
7773         // for resizable we need to use actual sizes.
7774         var w = this.cm.getColumnWidth(this.cellIndex);
7775         if (!this.view.mainWrap) {
7776             // bootstrap.
7777             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7778         }
7779         
7780         
7781         
7782         // this was w-this.grid.minColumnWidth;
7783         // doesnt really make sense? - w = thie curren width or the rendered one?
7784         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7785         this.resetConstraints();
7786         this.setXConstraint(minw, 1000);
7787         this.setYConstraint(0, 0);
7788         this.minX = x - minw;
7789         this.maxX = x + 1000;
7790         this.startPos = x;
7791         if (!this.view.mainWrap) { // this is Bootstrap code..
7792             this.getDragEl().style.display='block';
7793         }
7794         
7795         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7796     },
7797
7798
7799     handleMouseDown : function(e){
7800         ev = Roo.EventObject.setEvent(e);
7801         var t = this.fly(ev.getTarget());
7802         if(t.hasClass("x-grid-split")){
7803             this.cellIndex = this.view.getCellIndex(t.dom);
7804             this.split = t.dom;
7805             this.cm = this.grid.colModel;
7806             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7807                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7808             }
7809         }
7810     },
7811
7812     endDrag : function(e){
7813         this.view.headersDisabled = false;
7814         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7815         var diff = endX - this.startPos;
7816         // 
7817         var w = this.cm.getColumnWidth(this.cellIndex);
7818         if (!this.view.mainWrap) {
7819             w = 0;
7820         }
7821         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7822     },
7823
7824     autoOffset : function(){
7825         this.setDelta(0,0);
7826     }
7827 });/*
7828  * Based on:
7829  * Ext JS Library 1.1.1
7830  * Copyright(c) 2006-2007, Ext JS, LLC.
7831  *
7832  * Originally Released Under LGPL - original licence link has changed is not relivant.
7833  *
7834  * Fork - LGPL
7835  * <script type="text/javascript">
7836  */
7837
7838 /**
7839  * @class Roo.grid.AbstractSelectionModel
7840  * @extends Roo.util.Observable
7841  * @abstract
7842  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7843  * implemented by descendant classes.  This class should not be directly instantiated.
7844  * @constructor
7845  */
7846 Roo.grid.AbstractSelectionModel = function(){
7847     this.locked = false;
7848     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7849 };
7850
7851 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7852     /** @ignore Called by the grid automatically. Do not call directly. */
7853     init : function(grid){
7854         this.grid = grid;
7855         this.initEvents();
7856     },
7857
7858     /**
7859      * Locks the selections.
7860      */
7861     lock : function(){
7862         this.locked = true;
7863     },
7864
7865     /**
7866      * Unlocks the selections.
7867      */
7868     unlock : function(){
7869         this.locked = false;
7870     },
7871
7872     /**
7873      * Returns true if the selections are locked.
7874      * @return {Boolean}
7875      */
7876     isLocked : function(){
7877         return this.locked;
7878     }
7879 });/*
7880  * Based on:
7881  * Ext JS Library 1.1.1
7882  * Copyright(c) 2006-2007, Ext JS, LLC.
7883  *
7884  * Originally Released Under LGPL - original licence link has changed is not relivant.
7885  *
7886  * Fork - LGPL
7887  * <script type="text/javascript">
7888  */
7889 /**
7890  * @extends Roo.grid.AbstractSelectionModel
7891  * @class Roo.grid.RowSelectionModel
7892  * The default SelectionModel used by {@link Roo.grid.Grid}.
7893  * It supports multiple selections and keyboard selection/navigation. 
7894  * @constructor
7895  * @param {Object} config
7896  */
7897 Roo.grid.RowSelectionModel = function(config){
7898     Roo.apply(this, config);
7899     this.selections = new Roo.util.MixedCollection(false, function(o){
7900         return o.id;
7901     });
7902
7903     this.last = false;
7904     this.lastActive = false;
7905
7906     this.addEvents({
7907         /**
7908         * @event selectionchange
7909         * Fires when the selection changes
7910         * @param {SelectionModel} this
7911         */
7912        "selectionchange" : true,
7913        /**
7914         * @event afterselectionchange
7915         * Fires after the selection changes (eg. by key press or clicking)
7916         * @param {SelectionModel} this
7917         */
7918        "afterselectionchange" : true,
7919        /**
7920         * @event beforerowselect
7921         * Fires when a row is selected being selected, return false to cancel.
7922         * @param {SelectionModel} this
7923         * @param {Number} rowIndex The selected index
7924         * @param {Boolean} keepExisting False if other selections will be cleared
7925         */
7926        "beforerowselect" : true,
7927        /**
7928         * @event rowselect
7929         * Fires when a row is selected.
7930         * @param {SelectionModel} this
7931         * @param {Number} rowIndex The selected index
7932         * @param {Roo.data.Record} r The record
7933         */
7934        "rowselect" : true,
7935        /**
7936         * @event rowdeselect
7937         * Fires when a row is deselected.
7938         * @param {SelectionModel} this
7939         * @param {Number} rowIndex The selected index
7940         */
7941         "rowdeselect" : true
7942     });
7943     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7944     this.locked = false;
7945 };
7946
7947 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7948     /**
7949      * @cfg {Boolean} singleSelect
7950      * True to allow selection of only one row at a time (defaults to false)
7951      */
7952     singleSelect : false,
7953
7954     // private
7955     initEvents : function(){
7956
7957         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7958             this.grid.on("mousedown", this.handleMouseDown, this);
7959         }else{ // allow click to work like normal
7960             this.grid.on("rowclick", this.handleDragableRowClick, this);
7961         }
7962         // bootstrap does not have a view..
7963         var view = this.grid.view ? this.grid.view : this.grid;
7964         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7965             "up" : function(e){
7966                 if(!e.shiftKey){
7967                     this.selectPrevious(e.shiftKey);
7968                 }else if(this.last !== false && this.lastActive !== false){
7969                     var last = this.last;
7970                     this.selectRange(this.last,  this.lastActive-1);
7971                     view.focusRow(this.lastActive);
7972                     if(last !== false){
7973                         this.last = last;
7974                     }
7975                 }else{
7976                     this.selectFirstRow();
7977                 }
7978                 this.fireEvent("afterselectionchange", this);
7979             },
7980             "down" : function(e){
7981                 if(!e.shiftKey){
7982                     this.selectNext(e.shiftKey);
7983                 }else if(this.last !== false && this.lastActive !== false){
7984                     var last = this.last;
7985                     this.selectRange(this.last,  this.lastActive+1);
7986                     view.focusRow(this.lastActive);
7987                     if(last !== false){
7988                         this.last = last;
7989                     }
7990                 }else{
7991                     this.selectFirstRow();
7992                 }
7993                 this.fireEvent("afterselectionchange", this);
7994             },
7995             scope: this
7996         });
7997
7998          
7999         view.on("refresh", this.onRefresh, this);
8000         view.on("rowupdated", this.onRowUpdated, this);
8001         view.on("rowremoved", this.onRemove, this);
8002     },
8003
8004     // private
8005     onRefresh : function(){
8006         var ds = this.grid.ds, i, v = this.grid.view;
8007         var s = this.selections;
8008         s.each(function(r){
8009             if((i = ds.indexOfId(r.id)) != -1){
8010                 v.onRowSelect(i);
8011                 s.add(ds.getAt(i)); // updating the selection relate data
8012             }else{
8013                 s.remove(r);
8014             }
8015         });
8016     },
8017
8018     // private
8019     onRemove : function(v, index, r){
8020         this.selections.remove(r);
8021     },
8022
8023     // private
8024     onRowUpdated : function(v, index, r){
8025         if(this.isSelected(r)){
8026             v.onRowSelect(index);
8027         }
8028     },
8029
8030     /**
8031      * Select records.
8032      * @param {Array} records The records to select
8033      * @param {Boolean} keepExisting (optional) True to keep existing selections
8034      */
8035     selectRecords : function(records, keepExisting){
8036         if(!keepExisting){
8037             this.clearSelections();
8038         }
8039         var ds = this.grid.ds;
8040         for(var i = 0, len = records.length; i < len; i++){
8041             this.selectRow(ds.indexOf(records[i]), true);
8042         }
8043     },
8044
8045     /**
8046      * Gets the number of selected rows.
8047      * @return {Number}
8048      */
8049     getCount : function(){
8050         return this.selections.length;
8051     },
8052
8053     /**
8054      * Selects the first row in the grid.
8055      */
8056     selectFirstRow : function(){
8057         this.selectRow(0);
8058     },
8059
8060     /**
8061      * Select the last row.
8062      * @param {Boolean} keepExisting (optional) True to keep existing selections
8063      */
8064     selectLastRow : function(keepExisting){
8065         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8066     },
8067
8068     /**
8069      * Selects the row immediately following the last selected row.
8070      * @param {Boolean} keepExisting (optional) True to keep existing selections
8071      */
8072     selectNext : function(keepExisting){
8073         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8074             this.selectRow(this.last+1, keepExisting);
8075             var view = this.grid.view ? this.grid.view : this.grid;
8076             view.focusRow(this.last);
8077         }
8078     },
8079
8080     /**
8081      * Selects the row that precedes the last selected row.
8082      * @param {Boolean} keepExisting (optional) True to keep existing selections
8083      */
8084     selectPrevious : function(keepExisting){
8085         if(this.last){
8086             this.selectRow(this.last-1, keepExisting);
8087             var view = this.grid.view ? this.grid.view : this.grid;
8088             view.focusRow(this.last);
8089         }
8090     },
8091
8092     /**
8093      * Returns the selected records
8094      * @return {Array} Array of selected records
8095      */
8096     getSelections : function(){
8097         return [].concat(this.selections.items);
8098     },
8099
8100     /**
8101      * Returns the first selected record.
8102      * @return {Record}
8103      */
8104     getSelected : function(){
8105         return this.selections.itemAt(0);
8106     },
8107
8108
8109     /**
8110      * Clears all selections.
8111      */
8112     clearSelections : function(fast){
8113         if(this.locked) {
8114             return;
8115         }
8116         if(fast !== true){
8117             var ds = this.grid.ds;
8118             var s = this.selections;
8119             s.each(function(r){
8120                 this.deselectRow(ds.indexOfId(r.id));
8121             }, this);
8122             s.clear();
8123         }else{
8124             this.selections.clear();
8125         }
8126         this.last = false;
8127     },
8128
8129
8130     /**
8131      * Selects all rows.
8132      */
8133     selectAll : function(){
8134         if(this.locked) {
8135             return;
8136         }
8137         this.selections.clear();
8138         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8139             this.selectRow(i, true);
8140         }
8141     },
8142
8143     /**
8144      * Returns True if there is a selection.
8145      * @return {Boolean}
8146      */
8147     hasSelection : function(){
8148         return this.selections.length > 0;
8149     },
8150
8151     /**
8152      * Returns True if the specified row is selected.
8153      * @param {Number/Record} record The record or index of the record to check
8154      * @return {Boolean}
8155      */
8156     isSelected : function(index){
8157         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8158         return (r && this.selections.key(r.id) ? true : false);
8159     },
8160
8161     /**
8162      * Returns True if the specified record id is selected.
8163      * @param {String} id The id of record to check
8164      * @return {Boolean}
8165      */
8166     isIdSelected : function(id){
8167         return (this.selections.key(id) ? true : false);
8168     },
8169
8170     // private
8171     handleMouseDown : function(e, t)
8172     {
8173         var view = this.grid.view ? this.grid.view : this.grid;
8174         var rowIndex;
8175         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8176             return;
8177         };
8178         if(e.shiftKey && this.last !== false){
8179             var last = this.last;
8180             this.selectRange(last, rowIndex, e.ctrlKey);
8181             this.last = last; // reset the last
8182             view.focusRow(rowIndex);
8183         }else{
8184             var isSelected = this.isSelected(rowIndex);
8185             if(e.button !== 0 && isSelected){
8186                 view.focusRow(rowIndex);
8187             }else if(e.ctrlKey && isSelected){
8188                 this.deselectRow(rowIndex);
8189             }else if(!isSelected){
8190                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8191                 view.focusRow(rowIndex);
8192             }
8193         }
8194         this.fireEvent("afterselectionchange", this);
8195     },
8196     // private
8197     handleDragableRowClick :  function(grid, rowIndex, e) 
8198     {
8199         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8200             this.selectRow(rowIndex, false);
8201             var view = this.grid.view ? this.grid.view : this.grid;
8202             view.focusRow(rowIndex);
8203              this.fireEvent("afterselectionchange", this);
8204         }
8205     },
8206     
8207     /**
8208      * Selects multiple rows.
8209      * @param {Array} rows Array of the indexes of the row to select
8210      * @param {Boolean} keepExisting (optional) True to keep existing selections
8211      */
8212     selectRows : function(rows, keepExisting){
8213         if(!keepExisting){
8214             this.clearSelections();
8215         }
8216         for(var i = 0, len = rows.length; i < len; i++){
8217             this.selectRow(rows[i], true);
8218         }
8219     },
8220
8221     /**
8222      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8223      * @param {Number} startRow The index of the first row in the range
8224      * @param {Number} endRow The index of the last row in the range
8225      * @param {Boolean} keepExisting (optional) True to retain existing selections
8226      */
8227     selectRange : function(startRow, endRow, keepExisting){
8228         if(this.locked) {
8229             return;
8230         }
8231         if(!keepExisting){
8232             this.clearSelections();
8233         }
8234         if(startRow <= endRow){
8235             for(var i = startRow; i <= endRow; i++){
8236                 this.selectRow(i, true);
8237             }
8238         }else{
8239             for(var i = startRow; i >= endRow; i--){
8240                 this.selectRow(i, true);
8241             }
8242         }
8243     },
8244
8245     /**
8246      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8247      * @param {Number} startRow The index of the first row in the range
8248      * @param {Number} endRow The index of the last row in the range
8249      */
8250     deselectRange : function(startRow, endRow, preventViewNotify){
8251         if(this.locked) {
8252             return;
8253         }
8254         for(var i = startRow; i <= endRow; i++){
8255             this.deselectRow(i, preventViewNotify);
8256         }
8257     },
8258
8259     /**
8260      * Selects a row.
8261      * @param {Number} row The index of the row to select
8262      * @param {Boolean} keepExisting (optional) True to keep existing selections
8263      */
8264     selectRow : function(index, keepExisting, preventViewNotify){
8265         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8266             return;
8267         }
8268         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8269             if(!keepExisting || this.singleSelect){
8270                 this.clearSelections();
8271             }
8272             var r = this.grid.ds.getAt(index);
8273             this.selections.add(r);
8274             this.last = this.lastActive = index;
8275             if(!preventViewNotify){
8276                 var view = this.grid.view ? this.grid.view : this.grid;
8277                 view.onRowSelect(index);
8278             }
8279             this.fireEvent("rowselect", this, index, r);
8280             this.fireEvent("selectionchange", this);
8281         }
8282     },
8283
8284     /**
8285      * Deselects a row.
8286      * @param {Number} row The index of the row to deselect
8287      */
8288     deselectRow : function(index, preventViewNotify){
8289         if(this.locked) {
8290             return;
8291         }
8292         if(this.last == index){
8293             this.last = false;
8294         }
8295         if(this.lastActive == index){
8296             this.lastActive = false;
8297         }
8298         var r = this.grid.ds.getAt(index);
8299         this.selections.remove(r);
8300         if(!preventViewNotify){
8301             var view = this.grid.view ? this.grid.view : this.grid;
8302             view.onRowDeselect(index);
8303         }
8304         this.fireEvent("rowdeselect", this, index);
8305         this.fireEvent("selectionchange", this);
8306     },
8307
8308     // private
8309     restoreLast : function(){
8310         if(this._last){
8311             this.last = this._last;
8312         }
8313     },
8314
8315     // private
8316     acceptsNav : function(row, col, cm){
8317         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8318     },
8319
8320     // private
8321     onEditorKey : function(field, e){
8322         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8323         if(k == e.TAB){
8324             e.stopEvent();
8325             ed.completeEdit();
8326             if(e.shiftKey){
8327                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8328             }else{
8329                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8330             }
8331         }else if(k == e.ENTER && !e.ctrlKey){
8332             e.stopEvent();
8333             ed.completeEdit();
8334             if(e.shiftKey){
8335                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8336             }else{
8337                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8338             }
8339         }else if(k == e.ESC){
8340             ed.cancelEdit();
8341         }
8342         if(newCell){
8343             g.startEditing(newCell[0], newCell[1]);
8344         }
8345     }
8346 });/*
8347  * Based on:
8348  * Ext JS Library 1.1.1
8349  * Copyright(c) 2006-2007, Ext JS, LLC.
8350  *
8351  * Originally Released Under LGPL - original licence link has changed is not relivant.
8352  *
8353  * Fork - LGPL
8354  * <script type="text/javascript">
8355  */
8356  
8357
8358 /**
8359  * @class Roo.grid.ColumnModel
8360  * @extends Roo.util.Observable
8361  * This is the default implementation of a ColumnModel used by the Grid. It defines
8362  * the columns in the grid.
8363  * <br>Usage:<br>
8364  <pre><code>
8365  var colModel = new Roo.grid.ColumnModel([
8366         {header: "Ticker", width: 60, sortable: true, locked: true},
8367         {header: "Company Name", width: 150, sortable: true},
8368         {header: "Market Cap.", width: 100, sortable: true},
8369         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8370         {header: "Employees", width: 100, sortable: true, resizable: false}
8371  ]);
8372  </code></pre>
8373  * <p>
8374  
8375  * The config options listed for this class are options which may appear in each
8376  * individual column definition.
8377  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8378  * @constructor
8379  * @param {Object} config An Array of column config objects. See this class's
8380  * config objects for details.
8381 */
8382 Roo.grid.ColumnModel = function(config){
8383         /**
8384      * The config passed into the constructor
8385      */
8386     this.config = []; //config;
8387     this.lookup = {};
8388
8389     // if no id, create one
8390     // if the column does not have a dataIndex mapping,
8391     // map it to the order it is in the config
8392     for(var i = 0, len = config.length; i < len; i++){
8393         this.addColumn(config[i]);
8394         
8395     }
8396
8397     /**
8398      * The width of columns which have no width specified (defaults to 100)
8399      * @type Number
8400      */
8401     this.defaultWidth = 100;
8402
8403     /**
8404      * Default sortable of columns which have no sortable specified (defaults to false)
8405      * @type Boolean
8406      */
8407     this.defaultSortable = false;
8408
8409     this.addEvents({
8410         /**
8411              * @event widthchange
8412              * Fires when the width of a column changes.
8413              * @param {ColumnModel} this
8414              * @param {Number} columnIndex The column index
8415              * @param {Number} newWidth The new width
8416              */
8417             "widthchange": true,
8418         /**
8419              * @event headerchange
8420              * Fires when the text of a header changes.
8421              * @param {ColumnModel} this
8422              * @param {Number} columnIndex The column index
8423              * @param {Number} newText The new header text
8424              */
8425             "headerchange": true,
8426         /**
8427              * @event hiddenchange
8428              * Fires when a column is hidden or "unhidden".
8429              * @param {ColumnModel} this
8430              * @param {Number} columnIndex The column index
8431              * @param {Boolean} hidden true if hidden, false otherwise
8432              */
8433             "hiddenchange": true,
8434             /**
8435          * @event columnmoved
8436          * Fires when a column is moved.
8437          * @param {ColumnModel} this
8438          * @param {Number} oldIndex
8439          * @param {Number} newIndex
8440          */
8441         "columnmoved" : true,
8442         /**
8443          * @event columlockchange
8444          * Fires when a column's locked state is changed
8445          * @param {ColumnModel} this
8446          * @param {Number} colIndex
8447          * @param {Boolean} locked true if locked
8448          */
8449         "columnlockchange" : true
8450     });
8451     Roo.grid.ColumnModel.superclass.constructor.call(this);
8452 };
8453 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8454     /**
8455      * @cfg {String} header The header text to display in the Grid view.
8456      */
8457         /**
8458      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8459      */
8460         /**
8461      * @cfg {String} smHeader Header at Bootsrap Small width
8462      */
8463         /**
8464      * @cfg {String} mdHeader Header at Bootsrap Medium width
8465      */
8466         /**
8467      * @cfg {String} lgHeader Header at Bootsrap Large width
8468      */
8469         /**
8470      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8471      */
8472     /**
8473      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8474      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8475      * specified, the column's index is used as an index into the Record's data Array.
8476      */
8477     /**
8478      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8479      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8480      */
8481     /**
8482      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8483      * Defaults to the value of the {@link #defaultSortable} property.
8484      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8485      */
8486     /**
8487      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8488      */
8489     /**
8490      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8491      */
8492     /**
8493      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8494      */
8495     /**
8496      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8497      */
8498     /**
8499      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8500      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8501      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8502      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8503      */
8504        /**
8505      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8506      */
8507     /**
8508      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8509      */
8510     /**
8511      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8512      */
8513     /**
8514      * @cfg {String} cursor (Optional)
8515      */
8516     /**
8517      * @cfg {String} tooltip (Optional)
8518      */
8519     /**
8520      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8521      */
8522     /**
8523      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8524      */
8525     /**
8526      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8527      */
8528     /**
8529      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8530      */
8531         /**
8532      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8533      */
8534     /**
8535      * Returns the id of the column at the specified index.
8536      * @param {Number} index The column index
8537      * @return {String} the id
8538      */
8539     getColumnId : function(index){
8540         return this.config[index].id;
8541     },
8542
8543     /**
8544      * Returns the column for a specified id.
8545      * @param {String} id The column id
8546      * @return {Object} the column
8547      */
8548     getColumnById : function(id){
8549         return this.lookup[id];
8550     },
8551
8552     
8553     /**
8554      * Returns the column Object for a specified dataIndex.
8555      * @param {String} dataIndex The column dataIndex
8556      * @return {Object|Boolean} the column or false if not found
8557      */
8558     getColumnByDataIndex: function(dataIndex){
8559         var index = this.findColumnIndex(dataIndex);
8560         return index > -1 ? this.config[index] : false;
8561     },
8562     
8563     /**
8564      * Returns the index for a specified column id.
8565      * @param {String} id The column id
8566      * @return {Number} the index, or -1 if not found
8567      */
8568     getIndexById : function(id){
8569         for(var i = 0, len = this.config.length; i < len; i++){
8570             if(this.config[i].id == id){
8571                 return i;
8572             }
8573         }
8574         return -1;
8575     },
8576     
8577     /**
8578      * Returns the index for a specified column dataIndex.
8579      * @param {String} dataIndex The column dataIndex
8580      * @return {Number} the index, or -1 if not found
8581      */
8582     
8583     findColumnIndex : function(dataIndex){
8584         for(var i = 0, len = this.config.length; i < len; i++){
8585             if(this.config[i].dataIndex == dataIndex){
8586                 return i;
8587             }
8588         }
8589         return -1;
8590     },
8591     
8592     
8593     moveColumn : function(oldIndex, newIndex){
8594         var c = this.config[oldIndex];
8595         this.config.splice(oldIndex, 1);
8596         this.config.splice(newIndex, 0, c);
8597         this.dataMap = null;
8598         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8599     },
8600
8601     isLocked : function(colIndex){
8602         return this.config[colIndex].locked === true;
8603     },
8604
8605     setLocked : function(colIndex, value, suppressEvent){
8606         if(this.isLocked(colIndex) == value){
8607             return;
8608         }
8609         this.config[colIndex].locked = value;
8610         if(!suppressEvent){
8611             this.fireEvent("columnlockchange", this, colIndex, value);
8612         }
8613     },
8614
8615     getTotalLockedWidth : function(){
8616         var totalWidth = 0;
8617         for(var i = 0; i < this.config.length; i++){
8618             if(this.isLocked(i) && !this.isHidden(i)){
8619                 this.totalWidth += this.getColumnWidth(i);
8620             }
8621         }
8622         return totalWidth;
8623     },
8624
8625     getLockedCount : function(){
8626         for(var i = 0, len = this.config.length; i < len; i++){
8627             if(!this.isLocked(i)){
8628                 return i;
8629             }
8630         }
8631         
8632         return this.config.length;
8633     },
8634
8635     /**
8636      * Returns the number of columns.
8637      * @return {Number}
8638      */
8639     getColumnCount : function(visibleOnly){
8640         if(visibleOnly === true){
8641             var c = 0;
8642             for(var i = 0, len = this.config.length; i < len; i++){
8643                 if(!this.isHidden(i)){
8644                     c++;
8645                 }
8646             }
8647             return c;
8648         }
8649         return this.config.length;
8650     },
8651
8652     /**
8653      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8654      * @param {Function} fn
8655      * @param {Object} scope (optional)
8656      * @return {Array} result
8657      */
8658     getColumnsBy : function(fn, scope){
8659         var r = [];
8660         for(var i = 0, len = this.config.length; i < len; i++){
8661             var c = this.config[i];
8662             if(fn.call(scope||this, c, i) === true){
8663                 r[r.length] = c;
8664             }
8665         }
8666         return r;
8667     },
8668
8669     /**
8670      * Returns true if the specified column is sortable.
8671      * @param {Number} col The column index
8672      * @return {Boolean}
8673      */
8674     isSortable : function(col){
8675         if(typeof this.config[col].sortable == "undefined"){
8676             return this.defaultSortable;
8677         }
8678         return this.config[col].sortable;
8679     },
8680
8681     /**
8682      * Returns the rendering (formatting) function defined for the column.
8683      * @param {Number} col The column index.
8684      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8685      */
8686     getRenderer : function(col){
8687         if(!this.config[col].renderer){
8688             return Roo.grid.ColumnModel.defaultRenderer;
8689         }
8690         return this.config[col].renderer;
8691     },
8692
8693     /**
8694      * Sets the rendering (formatting) function for a column.
8695      * @param {Number} col The column index
8696      * @param {Function} fn The function to use to process the cell's raw data
8697      * to return HTML markup for the grid view. The render function is called with
8698      * the following parameters:<ul>
8699      * <li>Data value.</li>
8700      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8701      * <li>css A CSS style string to apply to the table cell.</li>
8702      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8703      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8704      * <li>Row index</li>
8705      * <li>Column index</li>
8706      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8707      */
8708     setRenderer : function(col, fn){
8709         this.config[col].renderer = fn;
8710     },
8711
8712     /**
8713      * Returns the width for the specified column.
8714      * @param {Number} col The column index
8715      * @param (optional) {String} gridSize bootstrap width size.
8716      * @return {Number}
8717      */
8718     getColumnWidth : function(col, gridSize)
8719         {
8720                 var cfg = this.config[col];
8721                 
8722                 if (typeof(gridSize) == 'undefined') {
8723                         return cfg.width * 1 || this.defaultWidth;
8724                 }
8725                 if (gridSize === false) { // if we set it..
8726                         return cfg.width || false;
8727                 }
8728                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8729                 
8730                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8731                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8732                                 continue;
8733                         }
8734                         return cfg[ sizes[i] ];
8735                 }
8736                 return 1;
8737                 
8738     },
8739
8740     /**
8741      * Sets the width for a column.
8742      * @param {Number} col The column index
8743      * @param {Number} width The new width
8744      */
8745     setColumnWidth : function(col, width, suppressEvent){
8746         this.config[col].width = width;
8747         this.totalWidth = null;
8748         if(!suppressEvent){
8749              this.fireEvent("widthchange", this, col, width);
8750         }
8751     },
8752
8753     /**
8754      * Returns the total width of all columns.
8755      * @param {Boolean} includeHidden True to include hidden column widths
8756      * @return {Number}
8757      */
8758     getTotalWidth : function(includeHidden){
8759         if(!this.totalWidth){
8760             this.totalWidth = 0;
8761             for(var i = 0, len = this.config.length; i < len; i++){
8762                 if(includeHidden || !this.isHidden(i)){
8763                     this.totalWidth += this.getColumnWidth(i);
8764                 }
8765             }
8766         }
8767         return this.totalWidth;
8768     },
8769
8770     /**
8771      * Returns the header for the specified column.
8772      * @param {Number} col The column index
8773      * @return {String}
8774      */
8775     getColumnHeader : function(col){
8776         return this.config[col].header;
8777     },
8778
8779     /**
8780      * Sets the header for a column.
8781      * @param {Number} col The column index
8782      * @param {String} header The new header
8783      */
8784     setColumnHeader : function(col, header){
8785         this.config[col].header = header;
8786         this.fireEvent("headerchange", this, col, header);
8787     },
8788
8789     /**
8790      * Returns the tooltip for the specified column.
8791      * @param {Number} col The column index
8792      * @return {String}
8793      */
8794     getColumnTooltip : function(col){
8795             return this.config[col].tooltip;
8796     },
8797     /**
8798      * Sets the tooltip for a column.
8799      * @param {Number} col The column index
8800      * @param {String} tooltip The new tooltip
8801      */
8802     setColumnTooltip : function(col, tooltip){
8803             this.config[col].tooltip = tooltip;
8804     },
8805
8806     /**
8807      * Returns the dataIndex for the specified column.
8808      * @param {Number} col The column index
8809      * @return {Number}
8810      */
8811     getDataIndex : function(col){
8812         return this.config[col].dataIndex;
8813     },
8814
8815     /**
8816      * Sets the dataIndex for a column.
8817      * @param {Number} col The column index
8818      * @param {Number} dataIndex The new dataIndex
8819      */
8820     setDataIndex : function(col, dataIndex){
8821         this.config[col].dataIndex = dataIndex;
8822     },
8823
8824     
8825     
8826     /**
8827      * Returns true if the cell is editable.
8828      * @param {Number} colIndex The column index
8829      * @param {Number} rowIndex The row index - this is nto actually used..?
8830      * @return {Boolean}
8831      */
8832     isCellEditable : function(colIndex, rowIndex){
8833         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8834     },
8835
8836     /**
8837      * Returns the editor defined for the cell/column.
8838      * return false or null to disable editing.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index
8841      * @return {Object}
8842      */
8843     getCellEditor : function(colIndex, rowIndex){
8844         return this.config[colIndex].editor;
8845     },
8846
8847     /**
8848      * Sets if a column is editable.
8849      * @param {Number} col The column index
8850      * @param {Boolean} editable True if the column is editable
8851      */
8852     setEditable : function(col, editable){
8853         this.config[col].editable = editable;
8854     },
8855
8856
8857     /**
8858      * Returns true if the column is hidden.
8859      * @param {Number} colIndex The column index
8860      * @return {Boolean}
8861      */
8862     isHidden : function(colIndex){
8863         return this.config[colIndex].hidden;
8864     },
8865
8866
8867     /**
8868      * Returns true if the column width cannot be changed
8869      */
8870     isFixed : function(colIndex){
8871         return this.config[colIndex].fixed;
8872     },
8873
8874     /**
8875      * Returns true if the column can be resized
8876      * @return {Boolean}
8877      */
8878     isResizable : function(colIndex){
8879         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8880     },
8881     /**
8882      * Sets if a column is hidden.
8883      * @param {Number} colIndex The column index
8884      * @param {Boolean} hidden True if the column is hidden
8885      */
8886     setHidden : function(colIndex, hidden){
8887         this.config[colIndex].hidden = hidden;
8888         this.totalWidth = null;
8889         this.fireEvent("hiddenchange", this, colIndex, hidden);
8890     },
8891
8892     /**
8893      * Sets the editor for a column.
8894      * @param {Number} col The column index
8895      * @param {Object} editor The editor object
8896      */
8897     setEditor : function(col, editor){
8898         this.config[col].editor = editor;
8899     },
8900     /**
8901      * Add a column (experimental...) - defaults to adding to the end..
8902      * @param {Object} config 
8903     */
8904     addColumn : function(c)
8905     {
8906     
8907         var i = this.config.length;
8908         this.config[i] = c;
8909         
8910         if(typeof c.dataIndex == "undefined"){
8911             c.dataIndex = i;
8912         }
8913         if(typeof c.renderer == "string"){
8914             c.renderer = Roo.util.Format[c.renderer];
8915         }
8916         if(typeof c.id == "undefined"){
8917             c.id = Roo.id();
8918         }
8919         if(c.editor && c.editor.xtype){
8920             c.editor  = Roo.factory(c.editor, Roo.grid);
8921         }
8922         if(c.editor && c.editor.isFormField){
8923             c.editor = new Roo.grid.GridEditor(c.editor);
8924         }
8925         this.lookup[c.id] = c;
8926     }
8927     
8928 });
8929
8930 Roo.grid.ColumnModel.defaultRenderer = function(value)
8931 {
8932     if(typeof value == "object") {
8933         return value;
8934     }
8935         if(typeof value == "string" && value.length < 1){
8936             return "&#160;";
8937         }
8938     
8939         return String.format("{0}", value);
8940 };
8941
8942 // Alias for backwards compatibility
8943 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8944 /*
8945  * Based on:
8946  * Ext JS Library 1.1.1
8947  * Copyright(c) 2006-2007, Ext JS, LLC.
8948  *
8949  * Originally Released Under LGPL - original licence link has changed is not relivant.
8950  *
8951  * Fork - LGPL
8952  * <script type="text/javascript">
8953  */
8954  
8955 /**
8956  * @class Roo.LoadMask
8957  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8958  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8959  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8960  * element's UpdateManager load indicator and will be destroyed after the initial load.
8961  * @constructor
8962  * Create a new LoadMask
8963  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8964  * @param {Object} config The config object
8965  */
8966 Roo.LoadMask = function(el, config){
8967     this.el = Roo.get(el);
8968     Roo.apply(this, config);
8969     if(this.store){
8970         this.store.on('beforeload', this.onBeforeLoad, this);
8971         this.store.on('load', this.onLoad, this);
8972         this.store.on('loadexception', this.onLoadException, this);
8973         this.removeMask = false;
8974     }else{
8975         var um = this.el.getUpdateManager();
8976         um.showLoadIndicator = false; // disable the default indicator
8977         um.on('beforeupdate', this.onBeforeLoad, this);
8978         um.on('update', this.onLoad, this);
8979         um.on('failure', this.onLoad, this);
8980         this.removeMask = true;
8981     }
8982 };
8983
8984 Roo.LoadMask.prototype = {
8985     /**
8986      * @cfg {Boolean} removeMask
8987      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8988      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8989      */
8990     removeMask : false,
8991     /**
8992      * @cfg {String} msg
8993      * The text to display in a centered loading message box (defaults to 'Loading...')
8994      */
8995     msg : 'Loading...',
8996     /**
8997      * @cfg {String} msgCls
8998      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8999      */
9000     msgCls : 'x-mask-loading',
9001
9002     /**
9003      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9004      * @type Boolean
9005      */
9006     disabled: false,
9007
9008     /**
9009      * Disables the mask to prevent it from being displayed
9010      */
9011     disable : function(){
9012        this.disabled = true;
9013     },
9014
9015     /**
9016      * Enables the mask so that it can be displayed
9017      */
9018     enable : function(){
9019         this.disabled = false;
9020     },
9021     
9022     onLoadException : function()
9023     {
9024         Roo.log(arguments);
9025         
9026         if (typeof(arguments[3]) != 'undefined') {
9027             Roo.MessageBox.alert("Error loading",arguments[3]);
9028         } 
9029         /*
9030         try {
9031             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9032                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9033             }   
9034         } catch(e) {
9035             
9036         }
9037         */
9038     
9039         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9040     },
9041     // private
9042     onLoad : function()
9043     {
9044         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9045     },
9046
9047     // private
9048     onBeforeLoad : function(){
9049         if(!this.disabled){
9050             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9051         }
9052     },
9053
9054     // private
9055     destroy : function(){
9056         if(this.store){
9057             this.store.un('beforeload', this.onBeforeLoad, this);
9058             this.store.un('load', this.onLoad, this);
9059             this.store.un('loadexception', this.onLoadException, this);
9060         }else{
9061             var um = this.el.getUpdateManager();
9062             um.un('beforeupdate', this.onBeforeLoad, this);
9063             um.un('update', this.onLoad, this);
9064             um.un('failure', this.onLoad, this);
9065         }
9066     }
9067 };/**
9068  * @class Roo.bootstrap.Table
9069  * @licence LGBL
9070  * @extends Roo.bootstrap.Component
9071  * @children Roo.bootstrap.TableBody
9072  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9073  * Similar to Roo.grid.Grid
9074  * <pre><code>
9075  var table = Roo.factory({
9076     xtype : 'Table',
9077     xns : Roo.bootstrap,
9078     autoSizeColumns: true,
9079     
9080     
9081     store : {
9082         xtype : 'Store',
9083         xns : Roo.data,
9084         remoteSort : true,
9085         sortInfo : { direction : 'ASC', field: 'name' },
9086         proxy : {
9087            xtype : 'HttpProxy',
9088            xns : Roo.data,
9089            method : 'GET',
9090            url : 'https://example.com/some.data.url.json'
9091         },
9092         reader : {
9093            xtype : 'JsonReader',
9094            xns : Roo.data,
9095            fields : [ 'id', 'name', whatever' ],
9096            id : 'id',
9097            root : 'data'
9098         }
9099     },
9100     cm : [
9101         {
9102             xtype : 'ColumnModel',
9103             xns : Roo.grid,
9104             align : 'center',
9105             cursor : 'pointer',
9106             dataIndex : 'is_in_group',
9107             header : "Name",
9108             sortable : true,
9109             renderer : function(v, x , r) {  
9110             
9111                 return String.format("{0}", v)
9112             }
9113             width : 3
9114         } // more columns..
9115     ],
9116     selModel : {
9117         xtype : 'RowSelectionModel',
9118         xns : Roo.bootstrap.Table
9119         // you can add listeners to catch selection change here....
9120     }
9121      
9122
9123  });
9124  // set any options
9125  grid.render(Roo.get("some-div"));
9126 </code></pre>
9127
9128 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9129
9130
9131
9132  *
9133  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9134  * @cfg {Roo.data.Store} store The data store to use
9135  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9136  * 
9137  * @cfg {String} cls table class
9138  *
9139  *
9140  * @cfg {string} empty_results  Text to display for no results 
9141  * @cfg {boolean} striped Should the rows be alternative striped
9142  * @cfg {boolean} bordered Add borders to the table
9143  * @cfg {boolean} hover Add hover highlighting
9144  * @cfg {boolean} condensed Format condensed
9145  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9146  *                also adds table-responsive (see bootstrap docs for details)
9147  * @cfg {Boolean} loadMask (true|false) default false
9148  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9149  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9150  * @cfg {Boolean} rowSelection (true|false) default false
9151  * @cfg {Boolean} cellSelection (true|false) default false
9152  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9153  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9154  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9155  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9156  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9157  *
9158  * 
9159  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9160  * 
9161  * @constructor
9162  * Create a new Table
9163  * @param {Object} config The config object
9164  */
9165
9166 Roo.bootstrap.Table = function(config)
9167 {
9168     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9169      
9170     // BC...
9171     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9172     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9173     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9174     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9175     
9176     this.view = this; // compat with grid.
9177     
9178     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9179     if (this.sm) {
9180         this.sm.grid = this;
9181         this.selModel = Roo.factory(this.sm, Roo.grid);
9182         this.sm = this.selModel;
9183         this.sm.xmodule = this.xmodule || false;
9184     }
9185     
9186     if (this.cm && typeof(this.cm.config) == 'undefined') {
9187         this.colModel = new Roo.grid.ColumnModel(this.cm);
9188         this.cm = this.colModel;
9189         this.cm.xmodule = this.xmodule || false;
9190     }
9191     if (this.store) {
9192         this.store= Roo.factory(this.store, Roo.data);
9193         this.ds = this.store;
9194         this.ds.xmodule = this.xmodule || false;
9195          
9196     }
9197     if (this.footer && this.store) {
9198         this.footer.dataSource = this.ds;
9199         this.footer = Roo.factory(this.footer);
9200     }
9201     
9202     /** @private */
9203     this.addEvents({
9204         /**
9205          * @event cellclick
9206          * Fires when a cell is clicked
9207          * @param {Roo.bootstrap.Table} this
9208          * @param {Roo.Element} el
9209          * @param {Number} rowIndex
9210          * @param {Number} columnIndex
9211          * @param {Roo.EventObject} e
9212          */
9213         "cellclick" : true,
9214         /**
9215          * @event celldblclick
9216          * Fires when a cell is double clicked
9217          * @param {Roo.bootstrap.Table} this
9218          * @param {Roo.Element} el
9219          * @param {Number} rowIndex
9220          * @param {Number} columnIndex
9221          * @param {Roo.EventObject} e
9222          */
9223         "celldblclick" : true,
9224         /**
9225          * @event rowclick
9226          * Fires when a row is clicked
9227          * @param {Roo.bootstrap.Table} this
9228          * @param {Roo.Element} el
9229          * @param {Number} rowIndex
9230          * @param {Roo.EventObject} e
9231          */
9232         "rowclick" : true,
9233         /**
9234          * @event rowdblclick
9235          * Fires when a row is double clicked
9236          * @param {Roo.bootstrap.Table} this
9237          * @param {Roo.Element} el
9238          * @param {Number} rowIndex
9239          * @param {Roo.EventObject} e
9240          */
9241         "rowdblclick" : true,
9242         /**
9243          * @event mouseover
9244          * Fires when a mouseover occur
9245          * @param {Roo.bootstrap.Table} this
9246          * @param {Roo.Element} el
9247          * @param {Number} rowIndex
9248          * @param {Number} columnIndex
9249          * @param {Roo.EventObject} e
9250          */
9251         "mouseover" : true,
9252         /**
9253          * @event mouseout
9254          * Fires when a mouseout occur
9255          * @param {Roo.bootstrap.Table} this
9256          * @param {Roo.Element} el
9257          * @param {Number} rowIndex
9258          * @param {Number} columnIndex
9259          * @param {Roo.EventObject} e
9260          */
9261         "mouseout" : true,
9262         /**
9263          * @event rowclass
9264          * Fires when a row is rendered, so you can change add a style to it.
9265          * @param {Roo.bootstrap.Table} this
9266          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9267          */
9268         'rowclass' : true,
9269           /**
9270          * @event rowsrendered
9271          * Fires when all the  rows have been rendered
9272          * @param {Roo.bootstrap.Table} this
9273          */
9274         'rowsrendered' : true,
9275         /**
9276          * @event contextmenu
9277          * The raw contextmenu event for the entire grid.
9278          * @param {Roo.EventObject} e
9279          */
9280         "contextmenu" : true,
9281         /**
9282          * @event rowcontextmenu
9283          * Fires when a row is right clicked
9284          * @param {Roo.bootstrap.Table} this
9285          * @param {Number} rowIndex
9286          * @param {Roo.EventObject} e
9287          */
9288         "rowcontextmenu" : true,
9289         /**
9290          * @event cellcontextmenu
9291          * Fires when a cell is right clicked
9292          * @param {Roo.bootstrap.Table} this
9293          * @param {Number} rowIndex
9294          * @param {Number} cellIndex
9295          * @param {Roo.EventObject} e
9296          */
9297          "cellcontextmenu" : true,
9298          /**
9299          * @event headercontextmenu
9300          * Fires when a header is right clicked
9301          * @param {Roo.bootstrap.Table} this
9302          * @param {Number} columnIndex
9303          * @param {Roo.EventObject} e
9304          */
9305         "headercontextmenu" : true,
9306         /**
9307          * @event mousedown
9308          * The raw mousedown event for the entire grid.
9309          * @param {Roo.EventObject} e
9310          */
9311         "mousedown" : true
9312         
9313     });
9314 };
9315
9316 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9317     
9318     cls: false,
9319     
9320     empty_results : '',
9321     striped : false,
9322     scrollBody : false,
9323     bordered: false,
9324     hover:  false,
9325     condensed : false,
9326     responsive : false,
9327     sm : false,
9328     cm : false,
9329     store : false,
9330     loadMask : false,
9331     footerShow : true,
9332     headerShow : true,
9333     enableColumnResize: true,
9334   
9335     rowSelection : false,
9336     cellSelection : false,
9337     layout : false,
9338
9339     minColumnWidth : 50,
9340     
9341     // Roo.Element - the tbody
9342     bodyEl: false,  // <tbody> Roo.Element - thead element    
9343     headEl: false,  // <thead> Roo.Element - thead element
9344     resizeProxy : false, // proxy element for dragging?
9345
9346
9347     
9348     container: false, // used by gridpanel...
9349     
9350     lazyLoad : false,
9351     
9352     CSS : Roo.util.CSS,
9353     
9354     auto_hide_footer : false,
9355     
9356     view: false, // actually points to this..
9357     
9358     getAutoCreate : function()
9359     {
9360         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9361         
9362         cfg = {
9363             tag: 'table',
9364             cls : 'table', 
9365             cn : []
9366         };
9367         // this get's auto added by panel.Grid
9368         if (this.scrollBody) {
9369             cfg.cls += ' table-body-fixed';
9370         }    
9371         if (this.striped) {
9372             cfg.cls += ' table-striped';
9373         }
9374         
9375         if (this.hover) {
9376             cfg.cls += ' table-hover';
9377         }
9378         if (this.bordered) {
9379             cfg.cls += ' table-bordered';
9380         }
9381         if (this.condensed) {
9382             cfg.cls += ' table-condensed';
9383         }
9384         
9385         if (this.responsive) {
9386             cfg.cls += ' table-responsive';
9387         }
9388         
9389         if (this.cls) {
9390             cfg.cls+=  ' ' +this.cls;
9391         }
9392         
9393         
9394         
9395         if (this.layout) {
9396             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9397         }
9398         
9399         if(this.store || this.cm){
9400             if(this.headerShow){
9401                 cfg.cn.push(this.renderHeader());
9402             }
9403             
9404             cfg.cn.push(this.renderBody());
9405             
9406             if(this.footerShow){
9407                 cfg.cn.push(this.renderFooter());
9408             }
9409             // where does this come from?
9410             //cfg.cls+=  ' TableGrid';
9411         }
9412         
9413         return { cn : [ cfg ] };
9414     },
9415     
9416     initEvents : function()
9417     {   
9418         if(!this.store || !this.cm){
9419             return;
9420         }
9421         if (this.selModel) {
9422             this.selModel.initEvents();
9423         }
9424         
9425         
9426         //Roo.log('initEvents with ds!!!!');
9427         
9428         this.bodyEl = this.el.select('tbody', true).first();
9429         this.headEl = this.el.select('thead', true).first();
9430         this.mainFoot = this.el.select('tfoot', true).first();
9431         
9432         
9433         
9434         
9435         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9436             e.on('click', this.sort, this);
9437         }, this);
9438         
9439         
9440         // why is this done????? = it breaks dialogs??
9441         //this.parent().el.setStyle('position', 'relative');
9442         
9443         
9444         if (this.footer) {
9445             this.footer.parentId = this.id;
9446             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9447             
9448             if(this.lazyLoad){
9449                 this.el.select('tfoot tr td').first().addClass('hide');
9450             }
9451         } 
9452         
9453         if(this.loadMask) {
9454             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9455         }
9456         
9457         this.store.on('load', this.onLoad, this);
9458         this.store.on('beforeload', this.onBeforeLoad, this);
9459         this.store.on('update', this.onUpdate, this);
9460         this.store.on('add', this.onAdd, this);
9461         this.store.on("clear", this.clear, this);
9462         
9463         this.el.on("contextmenu", this.onContextMenu, this);
9464         
9465         
9466         this.cm.on("headerchange", this.onHeaderChange, this);
9467         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9468
9469  //?? does bodyEl get replaced on render?
9470         this.bodyEl.on("click", this.onClick, this);
9471         this.bodyEl.on("dblclick", this.onDblClick, this);        
9472         this.bodyEl.on('scroll', this.onBodyScroll, this);
9473
9474         // guessing mainbody will work - this relays usually caught by selmodel at present.
9475         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9476   
9477   
9478         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9479         
9480   
9481         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9482             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9483         }
9484         
9485         this.initCSS();
9486     },
9487     // Compatibility with grid - we implement all the view features at present.
9488     getView : function()
9489     {
9490         return this;
9491     },
9492     
9493     initCSS : function()
9494     {
9495         
9496         
9497         var cm = this.cm, styles = [];
9498         this.CSS.removeStyleSheet(this.id + '-cssrules');
9499         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9500         // we can honour xs/sm/md/xl  as widths...
9501         // we first have to decide what widht we are currently at...
9502         var sz = Roo.getGridSize();
9503         
9504         var total = 0;
9505         var last = -1;
9506         var cols = []; // visable cols.
9507         var total_abs = 0;
9508         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9509             var w = cm.getColumnWidth(i, false);
9510             if(cm.isHidden(i)){
9511                 cols.push( { rel : false, abs : 0 });
9512                 continue;
9513             }
9514             if (w !== false) {
9515                 cols.push( { rel : false, abs : w });
9516                 total_abs += w;
9517                 last = i; // not really..
9518                 continue;
9519             }
9520             var w = cm.getColumnWidth(i, sz);
9521             if (w > 0) {
9522                 last = i
9523             }
9524             total += w;
9525             cols.push( { rel : w, abs : false });
9526         }
9527         
9528         var avail = this.bodyEl.dom.clientWidth - total_abs;
9529         
9530         var unitWidth = Math.floor(avail / total);
9531         var rem = avail - (unitWidth * total);
9532         
9533         var hidden, width, pos = 0 , splithide , left;
9534         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9535             
9536             hidden = 'display:none;';
9537             left = '';
9538             width  = 'width:0px;';
9539             splithide = '';
9540             if(!cm.isHidden(i)){
9541                 hidden = '';
9542                 
9543                 
9544                 // we can honour xs/sm/md/xl ?
9545                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9546                 if (w===0) {
9547                     hidden = 'display:none;';
9548                 }
9549                 // width should return a small number...
9550                 if (i == last) {
9551                     w+=rem; // add the remaining with..
9552                 }
9553                 pos += w;
9554                 left = "left:" + (pos -4) + "px;";
9555                 width = "width:" + w+ "px;";
9556                 
9557             }
9558             if (this.responsive) {
9559                 width = '';
9560                 left = '';
9561                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9562                 splithide = 'display: none;';
9563             }
9564             
9565             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9566             if (this.headEl) {
9567                 if (i == last) {
9568                     splithide = 'display:none;';
9569                 }
9570                 
9571                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9572                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9573                             // this is the popover version..
9574                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9575                 );
9576             }
9577             
9578         }
9579         //Roo.log(styles.join(''));
9580         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9581         
9582     },
9583     
9584     
9585     
9586     onContextMenu : function(e, t)
9587     {
9588         this.processEvent("contextmenu", e);
9589     },
9590     
9591     processEvent : function(name, e)
9592     {
9593         if (name != 'touchstart' ) {
9594             this.fireEvent(name, e);    
9595         }
9596         
9597         var t = e.getTarget();
9598         
9599         var cell = Roo.get(t);
9600         
9601         if(!cell){
9602             return;
9603         }
9604         
9605         if(cell.findParent('tfoot', false, true)){
9606             return;
9607         }
9608         
9609         if(cell.findParent('thead', false, true)){
9610             
9611             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9612                 cell = Roo.get(t).findParent('th', false, true);
9613                 if (!cell) {
9614                     Roo.log("failed to find th in thead?");
9615                     Roo.log(e.getTarget());
9616                     return;
9617                 }
9618             }
9619             
9620             var cellIndex = cell.dom.cellIndex;
9621             
9622             var ename = name == 'touchstart' ? 'click' : name;
9623             this.fireEvent("header" + ename, this, cellIndex, e);
9624             
9625             return;
9626         }
9627         
9628         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9629             cell = Roo.get(t).findParent('td', false, true);
9630             if (!cell) {
9631                 Roo.log("failed to find th in tbody?");
9632                 Roo.log(e.getTarget());
9633                 return;
9634             }
9635         }
9636         
9637         var row = cell.findParent('tr', false, true);
9638         var cellIndex = cell.dom.cellIndex;
9639         var rowIndex = row.dom.rowIndex - 1;
9640         
9641         if(row !== false){
9642             
9643             this.fireEvent("row" + name, this, rowIndex, e);
9644             
9645             if(cell !== false){
9646             
9647                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9648             }
9649         }
9650         
9651     },
9652     
9653     onMouseover : function(e, el)
9654     {
9655         var cell = Roo.get(el);
9656         
9657         if(!cell){
9658             return;
9659         }
9660         
9661         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9662             cell = cell.findParent('td', false, true);
9663         }
9664         
9665         var row = cell.findParent('tr', false, true);
9666         var cellIndex = cell.dom.cellIndex;
9667         var rowIndex = row.dom.rowIndex - 1; // start from 0
9668         
9669         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9670         
9671     },
9672     
9673     onMouseout : function(e, el)
9674     {
9675         var cell = Roo.get(el);
9676         
9677         if(!cell){
9678             return;
9679         }
9680         
9681         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9682             cell = cell.findParent('td', false, true);
9683         }
9684         
9685         var row = cell.findParent('tr', false, true);
9686         var cellIndex = cell.dom.cellIndex;
9687         var rowIndex = row.dom.rowIndex - 1; // start from 0
9688         
9689         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9690         
9691     },
9692     
9693     onClick : function(e, el)
9694     {
9695         var cell = Roo.get(el);
9696         
9697         if(!cell || (!this.cellSelection && !this.rowSelection)){
9698             return;
9699         }
9700         
9701         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9702             cell = cell.findParent('td', false, true);
9703         }
9704         
9705         if(!cell || typeof(cell) == 'undefined'){
9706             return;
9707         }
9708         
9709         var row = cell.findParent('tr', false, true);
9710         
9711         if(!row || typeof(row) == 'undefined'){
9712             return;
9713         }
9714         
9715         var cellIndex = cell.dom.cellIndex;
9716         var rowIndex = this.getRowIndex(row);
9717         
9718         // why??? - should these not be based on SelectionModel?
9719         //if(this.cellSelection){
9720             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9721         //}
9722         
9723         //if(this.rowSelection){
9724             this.fireEvent('rowclick', this, row, rowIndex, e);
9725         //}
9726          
9727     },
9728         
9729     onDblClick : function(e,el)
9730     {
9731         var cell = Roo.get(el);
9732         
9733         if(!cell || (!this.cellSelection && !this.rowSelection)){
9734             return;
9735         }
9736         
9737         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9738             cell = cell.findParent('td', false, true);
9739         }
9740         
9741         if(!cell || typeof(cell) == 'undefined'){
9742             return;
9743         }
9744         
9745         var row = cell.findParent('tr', false, true);
9746         
9747         if(!row || typeof(row) == 'undefined'){
9748             return;
9749         }
9750         
9751         var cellIndex = cell.dom.cellIndex;
9752         var rowIndex = this.getRowIndex(row);
9753         
9754         if(this.cellSelection){
9755             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9756         }
9757         
9758         if(this.rowSelection){
9759             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9760         }
9761     },
9762     findRowIndex : function(el)
9763     {
9764         var cell = Roo.get(el);
9765         if(!cell) {
9766             return false;
9767         }
9768         var row = cell.findParent('tr', false, true);
9769         
9770         if(!row || typeof(row) == 'undefined'){
9771             return false;
9772         }
9773         return this.getRowIndex(row);
9774     },
9775     sort : function(e,el)
9776     {
9777         var col = Roo.get(el);
9778         
9779         if(!col.hasClass('sortable')){
9780             return;
9781         }
9782         
9783         var sort = col.attr('sort');
9784         var dir = 'ASC';
9785         
9786         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9787             dir = 'DESC';
9788         }
9789         
9790         this.store.sortInfo = {field : sort, direction : dir};
9791         
9792         if (this.footer) {
9793             Roo.log("calling footer first");
9794             this.footer.onClick('first');
9795         } else {
9796         
9797             this.store.load({ params : { start : 0 } });
9798         }
9799     },
9800     
9801     renderHeader : function()
9802     {
9803         var header = {
9804             tag: 'thead',
9805             cn : []
9806         };
9807         
9808         var cm = this.cm;
9809         this.totalWidth = 0;
9810         
9811         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9812             
9813             var config = cm.config[i];
9814             
9815             var c = {
9816                 tag: 'th',
9817                 cls : 'x-hcol-' + i,
9818                 style : '',
9819                 
9820                 html: cm.getColumnHeader(i)
9821             };
9822             
9823             var tooltip = cm.getColumnTooltip(i);
9824             if (tooltip) {
9825                 c.tooltip = tooltip;
9826             }
9827             
9828             
9829             var hh = '';
9830             
9831             if(typeof(config.sortable) != 'undefined' && config.sortable){
9832                 c.cls += ' sortable';
9833                 c.html = '<i class="fa"></i>' + c.html;
9834             }
9835             
9836             // could use BS4 hidden-..-down 
9837             
9838             if(typeof(config.lgHeader) != 'undefined'){
9839                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9840             }
9841             
9842             if(typeof(config.mdHeader) != 'undefined'){
9843                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9844             }
9845             
9846             if(typeof(config.smHeader) != 'undefined'){
9847                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9848             }
9849             
9850             if(typeof(config.xsHeader) != 'undefined'){
9851                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9852             }
9853             
9854             if(hh.length){
9855                 c.html = hh;
9856             }
9857             
9858             if(typeof(config.tooltip) != 'undefined'){
9859                 c.tooltip = config.tooltip;
9860             }
9861             
9862             if(typeof(config.colspan) != 'undefined'){
9863                 c.colspan = config.colspan;
9864             }
9865             
9866             // hidden is handled by CSS now
9867             
9868             if(typeof(config.dataIndex) != 'undefined'){
9869                 c.sort = config.dataIndex;
9870             }
9871             
9872            
9873             
9874             if(typeof(config.align) != 'undefined' && config.align.length){
9875                 c.style += ' text-align:' + config.align + ';';
9876             }
9877             
9878             /* width is done in CSS
9879              *if(typeof(config.width) != 'undefined'){
9880                 c.style += ' width:' + config.width + 'px;';
9881                 this.totalWidth += config.width;
9882             } else {
9883                 this.totalWidth += 100; // assume minimum of 100 per column?
9884             }
9885             */
9886             
9887             if(typeof(config.cls) != 'undefined'){
9888                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9889             }
9890             // this is the bit that doesnt reall work at all...
9891             
9892             if (this.responsive) {
9893                  
9894             
9895                 ['xs','sm','md','lg'].map(function(size){
9896                     
9897                     if(typeof(config[size]) == 'undefined'){
9898                         return;
9899                     }
9900                      
9901                     if (!config[size]) { // 0 = hidden
9902                         // BS 4 '0' is treated as hide that column and below.
9903                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9904                         return;
9905                     }
9906                     
9907                     c.cls += ' col-' + size + '-' + config[size] + (
9908                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9909                     );
9910                     
9911                     
9912                 });
9913             }
9914             // at the end?
9915             
9916             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9917             
9918             
9919             
9920             
9921             header.cn.push(c)
9922         }
9923         
9924         return header;
9925     },
9926     
9927     renderBody : function()
9928     {
9929         var body = {
9930             tag: 'tbody',
9931             cn : [
9932                 {
9933                     tag: 'tr',
9934                     cn : [
9935                         {
9936                             tag : 'td',
9937                             colspan :  this.cm.getColumnCount()
9938                         }
9939                     ]
9940                 }
9941             ]
9942         };
9943         
9944         return body;
9945     },
9946     
9947     renderFooter : function()
9948     {
9949         var footer = {
9950             tag: 'tfoot',
9951             cn : [
9952                 {
9953                     tag: 'tr',
9954                     cn : [
9955                         {
9956                             tag : 'td',
9957                             colspan :  this.cm.getColumnCount()
9958                         }
9959                     ]
9960                 }
9961             ]
9962         };
9963         
9964         return footer;
9965     },
9966     
9967     
9968     
9969     onLoad : function()
9970     {
9971 //        Roo.log('ds onload');
9972         this.clear();
9973         
9974         var _this = this;
9975         var cm = this.cm;
9976         var ds = this.store;
9977         
9978         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9979             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9980             if (_this.store.sortInfo) {
9981                     
9982                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9983                     e.select('i', true).addClass(['fa-arrow-up']);
9984                 }
9985                 
9986                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9987                     e.select('i', true).addClass(['fa-arrow-down']);
9988                 }
9989             }
9990         });
9991         
9992         var tbody =  this.bodyEl;
9993               
9994         if(ds.getCount() > 0){
9995             ds.data.each(function(d,rowIndex){
9996                 var row =  this.renderRow(cm, ds, rowIndex);
9997                 
9998                 tbody.createChild(row);
9999                 
10000                 var _this = this;
10001                 
10002                 if(row.cellObjects.length){
10003                     Roo.each(row.cellObjects, function(r){
10004                         _this.renderCellObject(r);
10005                     })
10006                 }
10007                 
10008             }, this);
10009         } else if (this.empty_results.length) {
10010             this.el.mask(this.empty_results, 'no-spinner');
10011         }
10012         
10013         var tfoot = this.el.select('tfoot', true).first();
10014         
10015         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10016             
10017             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10018             
10019             var total = this.ds.getTotalCount();
10020             
10021             if(this.footer.pageSize < total){
10022                 this.mainFoot.show();
10023             }
10024         }
10025         
10026         Roo.each(this.el.select('tbody td', true).elements, function(e){
10027             e.on('mouseover', _this.onMouseover, _this);
10028         });
10029         
10030         Roo.each(this.el.select('tbody td', true).elements, function(e){
10031             e.on('mouseout', _this.onMouseout, _this);
10032         });
10033         this.fireEvent('rowsrendered', this);
10034         
10035         this.autoSize();
10036         
10037         this.initCSS(); /// resize cols
10038
10039         
10040     },
10041     
10042     
10043     onUpdate : function(ds,record)
10044     {
10045         this.refreshRow(record);
10046         this.autoSize();
10047     },
10048     
10049     onRemove : function(ds, record, index, isUpdate){
10050         if(isUpdate !== true){
10051             this.fireEvent("beforerowremoved", this, index, record);
10052         }
10053         var bt = this.bodyEl.dom;
10054         
10055         var rows = this.el.select('tbody > tr', true).elements;
10056         
10057         if(typeof(rows[index]) != 'undefined'){
10058             bt.removeChild(rows[index].dom);
10059         }
10060         
10061 //        if(bt.rows[index]){
10062 //            bt.removeChild(bt.rows[index]);
10063 //        }
10064         
10065         if(isUpdate !== true){
10066             //this.stripeRows(index);
10067             //this.syncRowHeights(index, index);
10068             //this.layout();
10069             this.fireEvent("rowremoved", this, index, record);
10070         }
10071     },
10072     
10073     onAdd : function(ds, records, rowIndex)
10074     {
10075         //Roo.log('on Add called');
10076         // - note this does not handle multiple adding very well..
10077         var bt = this.bodyEl.dom;
10078         for (var i =0 ; i < records.length;i++) {
10079             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10080             //Roo.log(records[i]);
10081             //Roo.log(this.store.getAt(rowIndex+i));
10082             this.insertRow(this.store, rowIndex + i, false);
10083             return;
10084         }
10085         
10086     },
10087     
10088     
10089     refreshRow : function(record){
10090         var ds = this.store, index;
10091         if(typeof record == 'number'){
10092             index = record;
10093             record = ds.getAt(index);
10094         }else{
10095             index = ds.indexOf(record);
10096             if (index < 0) {
10097                 return; // should not happen - but seems to 
10098             }
10099         }
10100         this.insertRow(ds, index, true);
10101         this.autoSize();
10102         this.onRemove(ds, record, index+1, true);
10103         this.autoSize();
10104         //this.syncRowHeights(index, index);
10105         //this.layout();
10106         this.fireEvent("rowupdated", this, index, record);
10107     },
10108     // private - called by RowSelection
10109     onRowSelect : function(rowIndex){
10110         var row = this.getRowDom(rowIndex);
10111         row.addClass(['bg-info','info']);
10112     },
10113     // private - called by RowSelection
10114     onRowDeselect : function(rowIndex)
10115     {
10116         if (rowIndex < 0) {
10117             return;
10118         }
10119         var row = this.getRowDom(rowIndex);
10120         row.removeClass(['bg-info','info']);
10121     },
10122       /**
10123      * Focuses the specified row.
10124      * @param {Number} row The row index
10125      */
10126     focusRow : function(row)
10127     {
10128         //Roo.log('GridView.focusRow');
10129         var x = this.bodyEl.dom.scrollLeft;
10130         this.focusCell(row, 0, false);
10131         this.bodyEl.dom.scrollLeft = x;
10132
10133     },
10134      /**
10135      * Focuses the specified cell.
10136      * @param {Number} row The row index
10137      * @param {Number} col The column index
10138      * @param {Boolean} hscroll false to disable horizontal scrolling
10139      */
10140     focusCell : function(row, col, hscroll)
10141     {
10142         //Roo.log('GridView.focusCell');
10143         var el = this.ensureVisible(row, col, hscroll);
10144         // not sure what focusEL achives = it's a <a> pos relative 
10145         //this.focusEl.alignTo(el, "tl-tl");
10146         //if(Roo.isGecko){
10147         //    this.focusEl.focus();
10148         //}else{
10149         //    this.focusEl.focus.defer(1, this.focusEl);
10150         //}
10151     },
10152     
10153      /**
10154      * Scrolls the specified cell into view
10155      * @param {Number} row The row index
10156      * @param {Number} col The column index
10157      * @param {Boolean} hscroll false to disable horizontal scrolling
10158      */
10159     ensureVisible : function(row, col, hscroll)
10160     {
10161         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10162         //return null; //disable for testing.
10163         if(typeof row != "number"){
10164             row = row.rowIndex;
10165         }
10166         if(row < 0 && row >= this.ds.getCount()){
10167             return  null;
10168         }
10169         col = (col !== undefined ? col : 0);
10170         var cm = this.cm;
10171         while(cm.isHidden(col)){
10172             col++;
10173         }
10174
10175         var el = this.getCellDom(row, col);
10176         if(!el){
10177             return null;
10178         }
10179         var c = this.bodyEl.dom;
10180
10181         var ctop = parseInt(el.offsetTop, 10);
10182         var cleft = parseInt(el.offsetLeft, 10);
10183         var cbot = ctop + el.offsetHeight;
10184         var cright = cleft + el.offsetWidth;
10185
10186         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10187         var ch = 0; //?? header is not withing the area?
10188         var stop = parseInt(c.scrollTop, 10);
10189         var sleft = parseInt(c.scrollLeft, 10);
10190         var sbot = stop + ch;
10191         var sright = sleft + c.clientWidth;
10192         /*
10193         Roo.log('GridView.ensureVisible:' +
10194                 ' ctop:' + ctop +
10195                 ' c.clientHeight:' + c.clientHeight +
10196                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10197                 ' stop:' + stop +
10198                 ' cbot:' + cbot +
10199                 ' sbot:' + sbot +
10200                 ' ch:' + ch  
10201                 );
10202         */
10203         if(ctop < stop){
10204             c.scrollTop = ctop;
10205             //Roo.log("set scrolltop to ctop DISABLE?");
10206         }else if(cbot > sbot){
10207             //Roo.log("set scrolltop to cbot-ch");
10208             c.scrollTop = cbot-ch;
10209         }
10210
10211         if(hscroll !== false){
10212             if(cleft < sleft){
10213                 c.scrollLeft = cleft;
10214             }else if(cright > sright){
10215                 c.scrollLeft = cright-c.clientWidth;
10216             }
10217         }
10218
10219         return el;
10220     },
10221     
10222     
10223     insertRow : function(dm, rowIndex, isUpdate){
10224         
10225         if(!isUpdate){
10226             this.fireEvent("beforerowsinserted", this, rowIndex);
10227         }
10228             //var s = this.getScrollState();
10229         var row = this.renderRow(this.cm, this.store, rowIndex);
10230         // insert before rowIndex..
10231         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10232         
10233         var _this = this;
10234                 
10235         if(row.cellObjects.length){
10236             Roo.each(row.cellObjects, function(r){
10237                 _this.renderCellObject(r);
10238             })
10239         }
10240             
10241         if(!isUpdate){
10242             this.fireEvent("rowsinserted", this, rowIndex);
10243             //this.syncRowHeights(firstRow, lastRow);
10244             //this.stripeRows(firstRow);
10245             //this.layout();
10246         }
10247         
10248     },
10249     
10250     
10251     getRowDom : function(rowIndex)
10252     {
10253         var rows = this.el.select('tbody > tr', true).elements;
10254         
10255         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10256         
10257     },
10258     getCellDom : function(rowIndex, colIndex)
10259     {
10260         var row = this.getRowDom(rowIndex);
10261         if (row === false) {
10262             return false;
10263         }
10264         var cols = row.select('td', true).elements;
10265         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10266         
10267     },
10268     
10269     // returns the object tree for a tr..
10270   
10271     
10272     renderRow : function(cm, ds, rowIndex) 
10273     {
10274         var d = ds.getAt(rowIndex);
10275         
10276         var row = {
10277             tag : 'tr',
10278             cls : 'x-row-' + rowIndex,
10279             cn : []
10280         };
10281             
10282         var cellObjects = [];
10283         
10284         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10285             var config = cm.config[i];
10286             
10287             var renderer = cm.getRenderer(i);
10288             var value = '';
10289             var id = false;
10290             
10291             if(typeof(renderer) !== 'undefined'){
10292                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10293             }
10294             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10295             // and are rendered into the cells after the row is rendered - using the id for the element.
10296             
10297             if(typeof(value) === 'object'){
10298                 id = Roo.id();
10299                 cellObjects.push({
10300                     container : id,
10301                     cfg : value 
10302                 })
10303             }
10304             
10305             var rowcfg = {
10306                 record: d,
10307                 rowIndex : rowIndex,
10308                 colIndex : i,
10309                 rowClass : ''
10310             };
10311
10312             this.fireEvent('rowclass', this, rowcfg);
10313             
10314             var td = {
10315                 tag: 'td',
10316                 // this might end up displaying HTML?
10317                 // this is too messy... - better to only do it on columsn you know are going to be too long
10318                 //tooltip : (typeof(value) === 'object') ? '' : value,
10319                 cls : rowcfg.rowClass + ' x-col-' + i,
10320                 style: '',
10321                 html: (typeof(value) === 'object') ? '' : value
10322             };
10323             
10324             if (id) {
10325                 td.id = id;
10326             }
10327             
10328             if(typeof(config.colspan) != 'undefined'){
10329                 td.colspan = config.colspan;
10330             }
10331             
10332             
10333             
10334             if(typeof(config.align) != 'undefined' && config.align.length){
10335                 td.style += ' text-align:' + config.align + ';';
10336             }
10337             if(typeof(config.valign) != 'undefined' && config.valign.length){
10338                 td.style += ' vertical-align:' + config.valign + ';';
10339             }
10340             /*
10341             if(typeof(config.width) != 'undefined'){
10342                 td.style += ' width:' +  config.width + 'px;';
10343             }
10344             */
10345             
10346             if(typeof(config.cursor) != 'undefined'){
10347                 td.style += ' cursor:' +  config.cursor + ';';
10348             }
10349             
10350             if(typeof(config.cls) != 'undefined'){
10351                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10352             }
10353             if (this.responsive) {
10354                 ['xs','sm','md','lg'].map(function(size){
10355                     
10356                     if(typeof(config[size]) == 'undefined'){
10357                         return;
10358                     }
10359                     
10360                     
10361                       
10362                     if (!config[size]) { // 0 = hidden
10363                         // BS 4 '0' is treated as hide that column and below.
10364                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10365                         return;
10366                     }
10367                     
10368                     td.cls += ' col-' + size + '-' + config[size] + (
10369                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10370                     );
10371                      
10372     
10373                 });
10374             }
10375             row.cn.push(td);
10376            
10377         }
10378         
10379         row.cellObjects = cellObjects;
10380         
10381         return row;
10382           
10383     },
10384     
10385     
10386     
10387     onBeforeLoad : function()
10388     {
10389         this.el.unmask(); // if needed.
10390     },
10391      /**
10392      * Remove all rows
10393      */
10394     clear : function()
10395     {
10396         this.el.select('tbody', true).first().dom.innerHTML = '';
10397     },
10398     /**
10399      * Show or hide a row.
10400      * @param {Number} rowIndex to show or hide
10401      * @param {Boolean} state hide
10402      */
10403     setRowVisibility : function(rowIndex, state)
10404     {
10405         var bt = this.bodyEl.dom;
10406         
10407         var rows = this.el.select('tbody > tr', true).elements;
10408         
10409         if(typeof(rows[rowIndex]) == 'undefined'){
10410             return;
10411         }
10412         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10413         
10414     },
10415     
10416     
10417     getSelectionModel : function(){
10418         if(!this.selModel){
10419             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10420         }
10421         return this.selModel;
10422     },
10423     /*
10424      * Render the Roo.bootstrap object from renderder
10425      */
10426     renderCellObject : function(r)
10427     {
10428         var _this = this;
10429         
10430         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10431         
10432         var t = r.cfg.render(r.container);
10433         
10434         if(r.cfg.cn){
10435             Roo.each(r.cfg.cn, function(c){
10436                 var child = {
10437                     container: t.getChildContainer(),
10438                     cfg: c
10439                 };
10440                 _this.renderCellObject(child);
10441             })
10442         }
10443     },
10444     /**
10445      * get the Row Index from a dom element.
10446      * @param {Roo.Element} row The row to look for
10447      * @returns {Number} the row
10448      */
10449     getRowIndex : function(row)
10450     {
10451         var rowIndex = -1;
10452         
10453         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10454             if(el != row){
10455                 return;
10456             }
10457             
10458             rowIndex = index;
10459         });
10460         
10461         return rowIndex;
10462     },
10463     /**
10464      * get the header TH element for columnIndex
10465      * @param {Number} columnIndex
10466      * @returns {Roo.Element}
10467      */
10468     getHeaderIndex: function(colIndex)
10469     {
10470         var cols = this.headEl.select('th', true).elements;
10471         return cols[colIndex]; 
10472     },
10473     /**
10474      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10475      * @param {domElement} cell to look for
10476      * @returns {Number} the column
10477      */
10478     getCellIndex : function(cell)
10479     {
10480         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10481         if(id){
10482             return parseInt(id[1], 10);
10483         }
10484         return 0;
10485     },
10486      /**
10487      * Returns the grid's underlying element = used by panel.Grid
10488      * @return {Element} The element
10489      */
10490     getGridEl : function(){
10491         return this.el;
10492     },
10493      /**
10494      * Forces a resize - used by panel.Grid
10495      * @return {Element} The element
10496      */
10497     autoSize : function()
10498     {
10499         //var ctr = Roo.get(this.container.dom.parentElement);
10500         var ctr = Roo.get(this.el.dom);
10501         
10502         var thd = this.getGridEl().select('thead',true).first();
10503         var tbd = this.getGridEl().select('tbody', true).first();
10504         var tfd = this.getGridEl().select('tfoot', true).first();
10505         
10506         var cw = ctr.getWidth();
10507         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10508         
10509         if (tbd) {
10510             
10511             tbd.setWidth(ctr.getWidth());
10512             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10513             // this needs fixing for various usage - currently only hydra job advers I think..
10514             //tdb.setHeight(
10515             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10516             //); 
10517             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10518             cw -= barsize;
10519         }
10520         cw = Math.max(cw, this.totalWidth);
10521         this.getGridEl().select('tbody tr',true).setWidth(cw);
10522         this.initCSS();
10523         
10524         // resize 'expandable coloumn?
10525         
10526         return; // we doe not have a view in this design..
10527         
10528     },
10529     onBodyScroll: function()
10530     {
10531         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10532         if(this.headEl){
10533             this.headEl.setStyle({
10534                 'position' : 'relative',
10535                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10536             });
10537         }
10538         
10539         if(this.lazyLoad){
10540             
10541             var scrollHeight = this.bodyEl.dom.scrollHeight;
10542             
10543             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10544             
10545             var height = this.bodyEl.getHeight();
10546             
10547             if(scrollHeight - height == scrollTop) {
10548                 
10549                 var total = this.ds.getTotalCount();
10550                 
10551                 if(this.footer.cursor + this.footer.pageSize < total){
10552                     
10553                     this.footer.ds.load({
10554                         params : {
10555                             start : this.footer.cursor + this.footer.pageSize,
10556                             limit : this.footer.pageSize
10557                         },
10558                         add : true
10559                     });
10560                 }
10561             }
10562             
10563         }
10564     },
10565     onColumnSplitterMoved : function(i, diff)
10566     {
10567         this.userResized = true;
10568         
10569         var cm = this.colModel;
10570         
10571         var w = this.getHeaderIndex(i).getWidth() + diff;
10572         
10573         
10574         cm.setColumnWidth(i, w, true);
10575         this.initCSS();
10576         //var cid = cm.getColumnId(i); << not used in this version?
10577        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10578         
10579         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10580         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10581         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10582 */
10583         //this.updateSplitters();
10584         //this.layout(); << ??
10585         this.fireEvent("columnresize", i, w);
10586     },
10587     onHeaderChange : function()
10588     {
10589         var header = this.renderHeader();
10590         var table = this.el.select('table', true).first();
10591         
10592         this.headEl.remove();
10593         this.headEl = table.createChild(header, this.bodyEl, false);
10594         
10595         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10596             e.on('click', this.sort, this);
10597         }, this);
10598         
10599         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10600             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10601         }
10602         
10603     },
10604     
10605     onHiddenChange : function(colModel, colIndex, hidden)
10606     {
10607         /*
10608         this.cm.setHidden()
10609         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10610         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10611         
10612         this.CSS.updateRule(thSelector, "display", "");
10613         this.CSS.updateRule(tdSelector, "display", "");
10614         
10615         if(hidden){
10616             this.CSS.updateRule(thSelector, "display", "none");
10617             this.CSS.updateRule(tdSelector, "display", "none");
10618         }
10619         */
10620         // onload calls initCSS()
10621         this.onHeaderChange();
10622         this.onLoad();
10623     },
10624     
10625     setColumnWidth: function(col_index, width)
10626     {
10627         // width = "md-2 xs-2..."
10628         if(!this.colModel.config[col_index]) {
10629             return;
10630         }
10631         
10632         var w = width.split(" ");
10633         
10634         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10635         
10636         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10637         
10638         
10639         for(var j = 0; j < w.length; j++) {
10640             
10641             if(!w[j]) {
10642                 continue;
10643             }
10644             
10645             var size_cls = w[j].split("-");
10646             
10647             if(!Number.isInteger(size_cls[1] * 1)) {
10648                 continue;
10649             }
10650             
10651             if(!this.colModel.config[col_index][size_cls[0]]) {
10652                 continue;
10653             }
10654             
10655             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10656                 continue;
10657             }
10658             
10659             h_row[0].classList.replace(
10660                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10661                 "col-"+size_cls[0]+"-"+size_cls[1]
10662             );
10663             
10664             for(var i = 0; i < rows.length; i++) {
10665                 
10666                 var size_cls = w[j].split("-");
10667                 
10668                 if(!Number.isInteger(size_cls[1] * 1)) {
10669                     continue;
10670                 }
10671                 
10672                 if(!this.colModel.config[col_index][size_cls[0]]) {
10673                     continue;
10674                 }
10675                 
10676                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10677                     continue;
10678                 }
10679                 
10680                 rows[i].classList.replace(
10681                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10682                     "col-"+size_cls[0]+"-"+size_cls[1]
10683                 );
10684             }
10685             
10686             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10687         }
10688     }
10689 });
10690
10691 // currently only used to find the split on drag.. 
10692 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10693
10694 /**
10695  * @depricated
10696 */
10697 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10698 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10699 /*
10700  * - LGPL
10701  *
10702  * table cell
10703  * 
10704  */
10705
10706 /**
10707  * @class Roo.bootstrap.TableCell
10708  * @extends Roo.bootstrap.Component
10709  * @children Roo.bootstrap.Component
10710  * @parent Roo.bootstrap.TableRow
10711  * Bootstrap TableCell class
10712  * 
10713  * @cfg {String} html cell contain text
10714  * @cfg {String} cls cell class
10715  * @cfg {String} tag cell tag (td|th) default td
10716  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10717  * @cfg {String} align Aligns the content in a cell
10718  * @cfg {String} axis Categorizes cells
10719  * @cfg {String} bgcolor Specifies the background color of a cell
10720  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10721  * @cfg {Number} colspan Specifies the number of columns a cell should span
10722  * @cfg {String} headers Specifies one or more header cells a cell is related to
10723  * @cfg {Number} height Sets the height of a cell
10724  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10725  * @cfg {Number} rowspan Sets the number of rows a cell should span
10726  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10727  * @cfg {String} valign Vertical aligns the content in a cell
10728  * @cfg {Number} width Specifies the width of a cell
10729  * 
10730  * @constructor
10731  * Create a new TableCell
10732  * @param {Object} config The config object
10733  */
10734
10735 Roo.bootstrap.TableCell = function(config){
10736     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10737 };
10738
10739 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10740     
10741     html: false,
10742     cls: false,
10743     tag: false,
10744     abbr: false,
10745     align: false,
10746     axis: false,
10747     bgcolor: false,
10748     charoff: false,
10749     colspan: false,
10750     headers: false,
10751     height: false,
10752     nowrap: false,
10753     rowspan: false,
10754     scope: false,
10755     valign: false,
10756     width: false,
10757     
10758     
10759     getAutoCreate : function(){
10760         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10761         
10762         cfg = {
10763             tag: 'td'
10764         };
10765         
10766         if(this.tag){
10767             cfg.tag = this.tag;
10768         }
10769         
10770         if (this.html) {
10771             cfg.html=this.html
10772         }
10773         if (this.cls) {
10774             cfg.cls=this.cls
10775         }
10776         if (this.abbr) {
10777             cfg.abbr=this.abbr
10778         }
10779         if (this.align) {
10780             cfg.align=this.align
10781         }
10782         if (this.axis) {
10783             cfg.axis=this.axis
10784         }
10785         if (this.bgcolor) {
10786             cfg.bgcolor=this.bgcolor
10787         }
10788         if (this.charoff) {
10789             cfg.charoff=this.charoff
10790         }
10791         if (this.colspan) {
10792             cfg.colspan=this.colspan
10793         }
10794         if (this.headers) {
10795             cfg.headers=this.headers
10796         }
10797         if (this.height) {
10798             cfg.height=this.height
10799         }
10800         if (this.nowrap) {
10801             cfg.nowrap=this.nowrap
10802         }
10803         if (this.rowspan) {
10804             cfg.rowspan=this.rowspan
10805         }
10806         if (this.scope) {
10807             cfg.scope=this.scope
10808         }
10809         if (this.valign) {
10810             cfg.valign=this.valign
10811         }
10812         if (this.width) {
10813             cfg.width=this.width
10814         }
10815         
10816         
10817         return cfg;
10818     }
10819    
10820 });
10821
10822  
10823
10824  /*
10825  * - LGPL
10826  *
10827  * table row
10828  * 
10829  */
10830
10831 /**
10832  * @class Roo.bootstrap.TableRow
10833  * @extends Roo.bootstrap.Component
10834  * @children Roo.bootstrap.TableCell
10835  * @parent Roo.bootstrap.TableBody
10836  * Bootstrap TableRow class
10837  * @cfg {String} cls row class
10838  * @cfg {String} align Aligns the content in a table row
10839  * @cfg {String} bgcolor Specifies a background color for a table row
10840  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10841  * @cfg {String} valign Vertical aligns the content in a table row
10842  * 
10843  * @constructor
10844  * Create a new TableRow
10845  * @param {Object} config The config object
10846  */
10847
10848 Roo.bootstrap.TableRow = function(config){
10849     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10850 };
10851
10852 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10853     
10854     cls: false,
10855     align: false,
10856     bgcolor: false,
10857     charoff: false,
10858     valign: false,
10859     
10860     getAutoCreate : function(){
10861         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10862         
10863         cfg = {
10864             tag: 'tr'
10865         };
10866             
10867         if(this.cls){
10868             cfg.cls = this.cls;
10869         }
10870         if(this.align){
10871             cfg.align = this.align;
10872         }
10873         if(this.bgcolor){
10874             cfg.bgcolor = this.bgcolor;
10875         }
10876         if(this.charoff){
10877             cfg.charoff = this.charoff;
10878         }
10879         if(this.valign){
10880             cfg.valign = this.valign;
10881         }
10882         
10883         return cfg;
10884     }
10885    
10886 });
10887
10888  
10889
10890  /*
10891  * - LGPL
10892  *
10893  * table body
10894  * 
10895  */
10896
10897 /**
10898  * @class Roo.bootstrap.TableBody
10899  * @extends Roo.bootstrap.Component
10900  * @children Roo.bootstrap.TableRow
10901  * @parent Roo.bootstrap.Table
10902  * Bootstrap TableBody class
10903  * @cfg {String} cls element class
10904  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10905  * @cfg {String} align Aligns the content inside the element
10906  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10907  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10908  * 
10909  * @constructor
10910  * Create a new TableBody
10911  * @param {Object} config The config object
10912  */
10913
10914 Roo.bootstrap.TableBody = function(config){
10915     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10916 };
10917
10918 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10919     
10920     cls: false,
10921     tag: false,
10922     align: false,
10923     charoff: false,
10924     valign: false,
10925     
10926     getAutoCreate : function(){
10927         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10928         
10929         cfg = {
10930             tag: 'tbody'
10931         };
10932             
10933         if (this.cls) {
10934             cfg.cls=this.cls
10935         }
10936         if(this.tag){
10937             cfg.tag = this.tag;
10938         }
10939         
10940         if(this.align){
10941             cfg.align = this.align;
10942         }
10943         if(this.charoff){
10944             cfg.charoff = this.charoff;
10945         }
10946         if(this.valign){
10947             cfg.valign = this.valign;
10948         }
10949         
10950         return cfg;
10951     }
10952     
10953     
10954 //    initEvents : function()
10955 //    {
10956 //        
10957 //        if(!this.store){
10958 //            return;
10959 //        }
10960 //        
10961 //        this.store = Roo.factory(this.store, Roo.data);
10962 //        this.store.on('load', this.onLoad, this);
10963 //        
10964 //        this.store.load();
10965 //        
10966 //    },
10967 //    
10968 //    onLoad: function () 
10969 //    {   
10970 //        this.fireEvent('load', this);
10971 //    }
10972 //    
10973 //   
10974 });
10975
10976  
10977
10978  /*
10979  * Based on:
10980  * Ext JS Library 1.1.1
10981  * Copyright(c) 2006-2007, Ext JS, LLC.
10982  *
10983  * Originally Released Under LGPL - original licence link has changed is not relivant.
10984  *
10985  * Fork - LGPL
10986  * <script type="text/javascript">
10987  */
10988
10989 // as we use this in bootstrap.
10990 Roo.namespace('Roo.form');
10991  /**
10992  * @class Roo.form.Action
10993  * Internal Class used to handle form actions
10994  * @constructor
10995  * @param {Roo.form.BasicForm} el The form element or its id
10996  * @param {Object} config Configuration options
10997  */
10998
10999  
11000  
11001 // define the action interface
11002 Roo.form.Action = function(form, options){
11003     this.form = form;
11004     this.options = options || {};
11005 };
11006 /**
11007  * Client Validation Failed
11008  * @const 
11009  */
11010 Roo.form.Action.CLIENT_INVALID = 'client';
11011 /**
11012  * Server Validation Failed
11013  * @const 
11014  */
11015 Roo.form.Action.SERVER_INVALID = 'server';
11016  /**
11017  * Connect to Server Failed
11018  * @const 
11019  */
11020 Roo.form.Action.CONNECT_FAILURE = 'connect';
11021 /**
11022  * Reading Data from Server Failed
11023  * @const 
11024  */
11025 Roo.form.Action.LOAD_FAILURE = 'load';
11026
11027 Roo.form.Action.prototype = {
11028     type : 'default',
11029     failureType : undefined,
11030     response : undefined,
11031     result : undefined,
11032
11033     // interface method
11034     run : function(options){
11035
11036     },
11037
11038     // interface method
11039     success : function(response){
11040
11041     },
11042
11043     // interface method
11044     handleResponse : function(response){
11045
11046     },
11047
11048     // default connection failure
11049     failure : function(response){
11050         
11051         this.response = response;
11052         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11053         this.form.afterAction(this, false);
11054     },
11055
11056     processResponse : function(response){
11057         this.response = response;
11058         if(!response.responseText){
11059             return true;
11060         }
11061         this.result = this.handleResponse(response);
11062         return this.result;
11063     },
11064
11065     // utility functions used internally
11066     getUrl : function(appendParams){
11067         var url = this.options.url || this.form.url || this.form.el.dom.action;
11068         if(appendParams){
11069             var p = this.getParams();
11070             if(p){
11071                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11072             }
11073         }
11074         return url;
11075     },
11076
11077     getMethod : function(){
11078         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11079     },
11080
11081     getParams : function(){
11082         var bp = this.form.baseParams;
11083         var p = this.options.params;
11084         if(p){
11085             if(typeof p == "object"){
11086                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11087             }else if(typeof p == 'string' && bp){
11088                 p += '&' + Roo.urlEncode(bp);
11089             }
11090         }else if(bp){
11091             p = Roo.urlEncode(bp);
11092         }
11093         return p;
11094     },
11095
11096     createCallback : function(){
11097         return {
11098             success: this.success,
11099             failure: this.failure,
11100             scope: this,
11101             timeout: (this.form.timeout*1000),
11102             upload: this.form.fileUpload ? this.success : undefined
11103         };
11104     }
11105 };
11106
11107 Roo.form.Action.Submit = function(form, options){
11108     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11109 };
11110
11111 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11112     type : 'submit',
11113
11114     haveProgress : false,
11115     uploadComplete : false,
11116     
11117     // uploadProgress indicator.
11118     uploadProgress : function()
11119     {
11120         if (!this.form.progressUrl) {
11121             return;
11122         }
11123         
11124         if (!this.haveProgress) {
11125             Roo.MessageBox.progress("Uploading", "Uploading");
11126         }
11127         if (this.uploadComplete) {
11128            Roo.MessageBox.hide();
11129            return;
11130         }
11131         
11132         this.haveProgress = true;
11133    
11134         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11135         
11136         var c = new Roo.data.Connection();
11137         c.request({
11138             url : this.form.progressUrl,
11139             params: {
11140                 id : uid
11141             },
11142             method: 'GET',
11143             success : function(req){
11144                //console.log(data);
11145                 var rdata = false;
11146                 var edata;
11147                 try  {
11148                    rdata = Roo.decode(req.responseText)
11149                 } catch (e) {
11150                     Roo.log("Invalid data from server..");
11151                     Roo.log(edata);
11152                     return;
11153                 }
11154                 if (!rdata || !rdata.success) {
11155                     Roo.log(rdata);
11156                     Roo.MessageBox.alert(Roo.encode(rdata));
11157                     return;
11158                 }
11159                 var data = rdata.data;
11160                 
11161                 if (this.uploadComplete) {
11162                    Roo.MessageBox.hide();
11163                    return;
11164                 }
11165                    
11166                 if (data){
11167                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11168                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11169                     );
11170                 }
11171                 this.uploadProgress.defer(2000,this);
11172             },
11173        
11174             failure: function(data) {
11175                 Roo.log('progress url failed ');
11176                 Roo.log(data);
11177             },
11178             scope : this
11179         });
11180            
11181     },
11182     
11183     
11184     run : function()
11185     {
11186         // run get Values on the form, so it syncs any secondary forms.
11187         this.form.getValues();
11188         
11189         var o = this.options;
11190         var method = this.getMethod();
11191         var isPost = method == 'POST';
11192         if(o.clientValidation === false || this.form.isValid()){
11193             
11194             if (this.form.progressUrl) {
11195                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11196                     (new Date() * 1) + '' + Math.random());
11197                     
11198             } 
11199             
11200             
11201             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11202                 form:this.form.el.dom,
11203                 url:this.getUrl(!isPost),
11204                 method: method,
11205                 params:isPost ? this.getParams() : null,
11206                 isUpload: this.form.fileUpload,
11207                 formData : this.form.formData
11208             }));
11209             
11210             this.uploadProgress();
11211
11212         }else if (o.clientValidation !== false){ // client validation failed
11213             this.failureType = Roo.form.Action.CLIENT_INVALID;
11214             this.form.afterAction(this, false);
11215         }
11216     },
11217
11218     success : function(response)
11219     {
11220         this.uploadComplete= true;
11221         if (this.haveProgress) {
11222             Roo.MessageBox.hide();
11223         }
11224         
11225         
11226         var result = this.processResponse(response);
11227         if(result === true || result.success){
11228             this.form.afterAction(this, true);
11229             return;
11230         }
11231         if(result.errors){
11232             this.form.markInvalid(result.errors);
11233             this.failureType = Roo.form.Action.SERVER_INVALID;
11234         }
11235         this.form.afterAction(this, false);
11236     },
11237     failure : function(response)
11238     {
11239         this.uploadComplete= true;
11240         if (this.haveProgress) {
11241             Roo.MessageBox.hide();
11242         }
11243         
11244         this.response = response;
11245         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11246         this.form.afterAction(this, false);
11247     },
11248     
11249     handleResponse : function(response){
11250         if(this.form.errorReader){
11251             var rs = this.form.errorReader.read(response);
11252             var errors = [];
11253             if(rs.records){
11254                 for(var i = 0, len = rs.records.length; i < len; i++) {
11255                     var r = rs.records[i];
11256                     errors[i] = r.data;
11257                 }
11258             }
11259             if(errors.length < 1){
11260                 errors = null;
11261             }
11262             return {
11263                 success : rs.success,
11264                 errors : errors
11265             };
11266         }
11267         var ret = false;
11268         try {
11269             ret = Roo.decode(response.responseText);
11270         } catch (e) {
11271             ret = {
11272                 success: false,
11273                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11274                 errors : []
11275             };
11276         }
11277         return ret;
11278         
11279     }
11280 });
11281
11282
11283 Roo.form.Action.Load = function(form, options){
11284     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11285     this.reader = this.form.reader;
11286 };
11287
11288 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11289     type : 'load',
11290
11291     run : function(){
11292         
11293         Roo.Ajax.request(Roo.apply(
11294                 this.createCallback(), {
11295                     method:this.getMethod(),
11296                     url:this.getUrl(false),
11297                     params:this.getParams()
11298         }));
11299     },
11300
11301     success : function(response){
11302         
11303         var result = this.processResponse(response);
11304         if(result === true || !result.success || !result.data){
11305             this.failureType = Roo.form.Action.LOAD_FAILURE;
11306             this.form.afterAction(this, false);
11307             return;
11308         }
11309         this.form.clearInvalid();
11310         this.form.setValues(result.data);
11311         this.form.afterAction(this, true);
11312     },
11313
11314     handleResponse : function(response){
11315         if(this.form.reader){
11316             var rs = this.form.reader.read(response);
11317             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11318             return {
11319                 success : rs.success,
11320                 data : data
11321             };
11322         }
11323         return Roo.decode(response.responseText);
11324     }
11325 });
11326
11327 Roo.form.Action.ACTION_TYPES = {
11328     'load' : Roo.form.Action.Load,
11329     'submit' : Roo.form.Action.Submit
11330 };/*
11331  * - LGPL
11332  *
11333  * form
11334  *
11335  */
11336
11337 /**
11338  * @class Roo.bootstrap.form.Form
11339  * @extends Roo.bootstrap.Component
11340  * @children Roo.bootstrap.Component
11341  * Bootstrap Form class
11342  * @cfg {String} method  GET | POST (default POST)
11343  * @cfg {String} labelAlign top | left (default top)
11344  * @cfg {String} align left  | right - for navbars
11345  * @cfg {Boolean} loadMask load mask when submit (default true)
11346
11347  *
11348  * @constructor
11349  * Create a new Form
11350  * @param {Object} config The config object
11351  */
11352
11353
11354 Roo.bootstrap.form.Form = function(config){
11355     
11356     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11357     
11358     Roo.bootstrap.form.Form.popover.apply();
11359     
11360     this.addEvents({
11361         /**
11362          * @event clientvalidation
11363          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11364          * @param {Form} this
11365          * @param {Boolean} valid true if the form has passed client-side validation
11366          */
11367         clientvalidation: true,
11368         /**
11369          * @event beforeaction
11370          * Fires before any action is performed. Return false to cancel the action.
11371          * @param {Form} this
11372          * @param {Action} action The action to be performed
11373          */
11374         beforeaction: true,
11375         /**
11376          * @event actionfailed
11377          * Fires when an action fails.
11378          * @param {Form} this
11379          * @param {Action} action The action that failed
11380          */
11381         actionfailed : true,
11382         /**
11383          * @event actioncomplete
11384          * Fires when an action is completed.
11385          * @param {Form} this
11386          * @param {Action} action The action that completed
11387          */
11388         actioncomplete : true
11389     });
11390 };
11391
11392 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11393
11394      /**
11395      * @cfg {String} method
11396      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11397      */
11398     method : 'POST',
11399     /**
11400      * @cfg {String} url
11401      * The URL to use for form actions if one isn't supplied in the action options.
11402      */
11403     /**
11404      * @cfg {Boolean} fileUpload
11405      * Set to true if this form is a file upload.
11406      */
11407
11408     /**
11409      * @cfg {Object} baseParams
11410      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11411      */
11412
11413     /**
11414      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11415      */
11416     timeout: 30,
11417     /**
11418      * @cfg {Sting} align (left|right) for navbar forms
11419      */
11420     align : 'left',
11421
11422     // private
11423     activeAction : null,
11424
11425     /**
11426      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11427      * element by passing it or its id or mask the form itself by passing in true.
11428      * @type Mixed
11429      */
11430     waitMsgTarget : false,
11431
11432     loadMask : true,
11433     
11434     /**
11435      * @cfg {Boolean} errorMask (true|false) default false
11436      */
11437     errorMask : false,
11438     
11439     /**
11440      * @cfg {Number} maskOffset Default 100
11441      */
11442     maskOffset : 100,
11443     
11444     /**
11445      * @cfg {Boolean} maskBody
11446      */
11447     maskBody : false,
11448
11449     getAutoCreate : function(){
11450
11451         var cfg = {
11452             tag: 'form',
11453             method : this.method || 'POST',
11454             id : this.id || Roo.id(),
11455             cls : ''
11456         };
11457         if (this.parent().xtype.match(/^Nav/)) {
11458             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11459
11460         }
11461
11462         if (this.labelAlign == 'left' ) {
11463             cfg.cls += ' form-horizontal';
11464         }
11465
11466
11467         return cfg;
11468     },
11469     initEvents : function()
11470     {
11471         this.el.on('submit', this.onSubmit, this);
11472         // this was added as random key presses on the form where triggering form submit.
11473         this.el.on('keypress', function(e) {
11474             if (e.getCharCode() != 13) {
11475                 return true;
11476             }
11477             // we might need to allow it for textareas.. and some other items.
11478             // check e.getTarget().
11479
11480             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11481                 return true;
11482             }
11483
11484             Roo.log("keypress blocked");
11485
11486             e.preventDefault();
11487             return false;
11488         });
11489         
11490     },
11491     // private
11492     onSubmit : function(e){
11493         e.stopEvent();
11494     },
11495
11496      /**
11497      * Returns true if client-side validation on the form is successful.
11498      * @return Boolean
11499      */
11500     isValid : function(){
11501         var items = this.getItems();
11502         var valid = true;
11503         var target = false;
11504         
11505         items.each(function(f){
11506             
11507             if(f.validate()){
11508                 return;
11509             }
11510             
11511             Roo.log('invalid field: ' + f.name);
11512             
11513             valid = false;
11514
11515             if(!target && f.el.isVisible(true)){
11516                 target = f;
11517             }
11518            
11519         });
11520         
11521         if(this.errorMask && !valid){
11522             Roo.bootstrap.form.Form.popover.mask(this, target);
11523         }
11524         
11525         return valid;
11526     },
11527     
11528     /**
11529      * Returns true if any fields in this form have changed since their original load.
11530      * @return Boolean
11531      */
11532     isDirty : function(){
11533         var dirty = false;
11534         var items = this.getItems();
11535         items.each(function(f){
11536            if(f.isDirty()){
11537                dirty = true;
11538                return false;
11539            }
11540            return true;
11541         });
11542         return dirty;
11543     },
11544      /**
11545      * Performs a predefined action (submit or load) or custom actions you define on this form.
11546      * @param {String} actionName The name of the action type
11547      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11548      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11549      * accept other config options):
11550      * <pre>
11551 Property          Type             Description
11552 ----------------  ---------------  ----------------------------------------------------------------------------------
11553 url               String           The url for the action (defaults to the form's url)
11554 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11555 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11556 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11557                                    validate the form on the client (defaults to false)
11558      * </pre>
11559      * @return {BasicForm} this
11560      */
11561     doAction : function(action, options){
11562         if(typeof action == 'string'){
11563             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11564         }
11565         if(this.fireEvent('beforeaction', this, action) !== false){
11566             this.beforeAction(action);
11567             action.run.defer(100, action);
11568         }
11569         return this;
11570     },
11571
11572     // private
11573     beforeAction : function(action){
11574         var o = action.options;
11575         
11576         if(this.loadMask){
11577             
11578             if(this.maskBody){
11579                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11580             } else {
11581                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11582             }
11583         }
11584         // not really supported yet.. ??
11585
11586         //if(this.waitMsgTarget === true){
11587         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11588         //}else if(this.waitMsgTarget){
11589         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11590         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11591         //}else {
11592         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11593        // }
11594
11595     },
11596
11597     // private
11598     afterAction : function(action, success){
11599         this.activeAction = null;
11600         var o = action.options;
11601
11602         if(this.loadMask){
11603             
11604             if(this.maskBody){
11605                 Roo.get(document.body).unmask();
11606             } else {
11607                 this.el.unmask();
11608             }
11609         }
11610         
11611         //if(this.waitMsgTarget === true){
11612 //            this.el.unmask();
11613         //}else if(this.waitMsgTarget){
11614         //    this.waitMsgTarget.unmask();
11615         //}else{
11616         //    Roo.MessageBox.updateProgress(1);
11617         //    Roo.MessageBox.hide();
11618        // }
11619         //
11620         if(success){
11621             if(o.reset){
11622                 this.reset();
11623             }
11624             Roo.callback(o.success, o.scope, [this, action]);
11625             this.fireEvent('actioncomplete', this, action);
11626
11627         }else{
11628
11629             // failure condition..
11630             // we have a scenario where updates need confirming.
11631             // eg. if a locking scenario exists..
11632             // we look for { errors : { needs_confirm : true }} in the response.
11633             if (
11634                 (typeof(action.result) != 'undefined')  &&
11635                 (typeof(action.result.errors) != 'undefined')  &&
11636                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11637            ){
11638                 var _t = this;
11639                 Roo.log("not supported yet");
11640                  /*
11641
11642                 Roo.MessageBox.confirm(
11643                     "Change requires confirmation",
11644                     action.result.errorMsg,
11645                     function(r) {
11646                         if (r != 'yes') {
11647                             return;
11648                         }
11649                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11650                     }
11651
11652                 );
11653                 */
11654
11655
11656                 return;
11657             }
11658
11659             Roo.callback(o.failure, o.scope, [this, action]);
11660             // show an error message if no failed handler is set..
11661             if (!this.hasListener('actionfailed')) {
11662                 Roo.log("need to add dialog support");
11663                 /*
11664                 Roo.MessageBox.alert("Error",
11665                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11666                         action.result.errorMsg :
11667                         "Saving Failed, please check your entries or try again"
11668                 );
11669                 */
11670             }
11671
11672             this.fireEvent('actionfailed', this, action);
11673         }
11674
11675     },
11676     /**
11677      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11678      * @param {String} id The value to search for
11679      * @return Field
11680      */
11681     findField : function(id){
11682         var items = this.getItems();
11683         var field = items.get(id);
11684         if(!field){
11685              items.each(function(f){
11686                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11687                     field = f;
11688                     return false;
11689                 }
11690                 return true;
11691             });
11692         }
11693         return field || null;
11694     },
11695      /**
11696      * Mark fields in this form invalid in bulk.
11697      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11698      * @return {BasicForm} this
11699      */
11700     markInvalid : function(errors){
11701         if(errors instanceof Array){
11702             for(var i = 0, len = errors.length; i < len; i++){
11703                 var fieldError = errors[i];
11704                 var f = this.findField(fieldError.id);
11705                 if(f){
11706                     f.markInvalid(fieldError.msg);
11707                 }
11708             }
11709         }else{
11710             var field, id;
11711             for(id in errors){
11712                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11713                     field.markInvalid(errors[id]);
11714                 }
11715             }
11716         }
11717         //Roo.each(this.childForms || [], function (f) {
11718         //    f.markInvalid(errors);
11719         //});
11720
11721         return this;
11722     },
11723
11724     /**
11725      * Set values for fields in this form in bulk.
11726      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11727      * @return {BasicForm} this
11728      */
11729     setValues : function(values){
11730         if(values instanceof Array){ // array of objects
11731             for(var i = 0, len = values.length; i < len; i++){
11732                 var v = values[i];
11733                 var f = this.findField(v.id);
11734                 if(f){
11735                     f.setValue(v.value);
11736                     if(this.trackResetOnLoad){
11737                         f.originalValue = f.getValue();
11738                     }
11739                 }
11740             }
11741         }else{ // object hash
11742             var field, id;
11743             for(id in values){
11744                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11745
11746                     if (field.setFromData &&
11747                         field.valueField &&
11748                         field.displayField &&
11749                         // combos' with local stores can
11750                         // be queried via setValue()
11751                         // to set their value..
11752                         (field.store && !field.store.isLocal)
11753                         ) {
11754                         // it's a combo
11755                         var sd = { };
11756                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11757                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11758                         field.setFromData(sd);
11759
11760                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11761                         
11762                         field.setFromData(values);
11763                         
11764                     } else {
11765                         field.setValue(values[id]);
11766                     }
11767
11768
11769                     if(this.trackResetOnLoad){
11770                         field.originalValue = field.getValue();
11771                     }
11772                 }
11773             }
11774         }
11775
11776         //Roo.each(this.childForms || [], function (f) {
11777         //    f.setValues(values);
11778         //});
11779
11780         return this;
11781     },
11782
11783     /**
11784      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11785      * they are returned as an array.
11786      * @param {Boolean} asString
11787      * @return {Object}
11788      */
11789     getValues : function(asString){
11790         //if (this.childForms) {
11791             // copy values from the child forms
11792         //    Roo.each(this.childForms, function (f) {
11793         //        this.setValues(f.getValues());
11794         //    }, this);
11795         //}
11796
11797
11798
11799         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11800         if(asString === true){
11801             return fs;
11802         }
11803         return Roo.urlDecode(fs);
11804     },
11805
11806     /**
11807      * Returns the fields in this form as an object with key/value pairs.
11808      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11809      * @return {Object}
11810      */
11811     getFieldValues : function(with_hidden)
11812     {
11813         var items = this.getItems();
11814         var ret = {};
11815         items.each(function(f){
11816             
11817             if (!f.getName()) {
11818                 return;
11819             }
11820             
11821             var v = f.getValue();
11822             
11823             if (f.inputType =='radio') {
11824                 if (typeof(ret[f.getName()]) == 'undefined') {
11825                     ret[f.getName()] = ''; // empty..
11826                 }
11827
11828                 if (!f.el.dom.checked) {
11829                     return;
11830
11831                 }
11832                 v = f.el.dom.value;
11833
11834             }
11835             
11836             if(f.xtype == 'MoneyField'){
11837                 ret[f.currencyName] = f.getCurrency();
11838             }
11839
11840             // not sure if this supported any more..
11841             if ((typeof(v) == 'object') && f.getRawValue) {
11842                 v = f.getRawValue() ; // dates..
11843             }
11844             // combo boxes where name != hiddenName...
11845             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11846                 ret[f.name] = f.getRawValue();
11847             }
11848             ret[f.getName()] = v;
11849         });
11850
11851         return ret;
11852     },
11853
11854     /**
11855      * Clears all invalid messages in this form.
11856      * @return {BasicForm} this
11857      */
11858     clearInvalid : function(){
11859         var items = this.getItems();
11860
11861         items.each(function(f){
11862            f.clearInvalid();
11863         });
11864
11865         return this;
11866     },
11867
11868     /**
11869      * Resets this form.
11870      * @return {BasicForm} this
11871      */
11872     reset : function(){
11873         var items = this.getItems();
11874         items.each(function(f){
11875             f.reset();
11876         });
11877
11878         Roo.each(this.childForms || [], function (f) {
11879             f.reset();
11880         });
11881
11882
11883         return this;
11884     },
11885     
11886     getItems : function()
11887     {
11888         var r=new Roo.util.MixedCollection(false, function(o){
11889             return o.id || (o.id = Roo.id());
11890         });
11891         var iter = function(el) {
11892             if (el.inputEl) {
11893                 r.add(el);
11894             }
11895             if (!el.items) {
11896                 return;
11897             }
11898             Roo.each(el.items,function(e) {
11899                 iter(e);
11900             });
11901         };
11902
11903         iter(this);
11904         return r;
11905     },
11906     
11907     hideFields : function(items)
11908     {
11909         Roo.each(items, function(i){
11910             
11911             var f = this.findField(i);
11912             
11913             if(!f){
11914                 return;
11915             }
11916             
11917             f.hide();
11918             
11919         }, this);
11920     },
11921     
11922     showFields : function(items)
11923     {
11924         Roo.each(items, function(i){
11925             
11926             var f = this.findField(i);
11927             
11928             if(!f){
11929                 return;
11930             }
11931             
11932             f.show();
11933             
11934         }, this);
11935     }
11936
11937 });
11938
11939 Roo.apply(Roo.bootstrap.form.Form, {
11940     
11941     popover : {
11942         
11943         padding : 5,
11944         
11945         isApplied : false,
11946         
11947         isMasked : false,
11948         
11949         form : false,
11950         
11951         target : false,
11952         
11953         toolTip : false,
11954         
11955         intervalID : false,
11956         
11957         maskEl : false,
11958         
11959         apply : function()
11960         {
11961             if(this.isApplied){
11962                 return;
11963             }
11964             
11965             this.maskEl = {
11966                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11967                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11968                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11969                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11970             };
11971             
11972             this.maskEl.top.enableDisplayMode("block");
11973             this.maskEl.left.enableDisplayMode("block");
11974             this.maskEl.bottom.enableDisplayMode("block");
11975             this.maskEl.right.enableDisplayMode("block");
11976             
11977             this.toolTip = new Roo.bootstrap.Tooltip({
11978                 cls : 'roo-form-error-popover',
11979                 alignment : {
11980                     'left' : ['r-l', [-2,0], 'right'],
11981                     'right' : ['l-r', [2,0], 'left'],
11982                     'bottom' : ['tl-bl', [0,2], 'top'],
11983                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11984                 }
11985             });
11986             
11987             this.toolTip.render(Roo.get(document.body));
11988
11989             this.toolTip.el.enableDisplayMode("block");
11990             
11991             Roo.get(document.body).on('click', function(){
11992                 this.unmask();
11993             }, this);
11994             
11995             Roo.get(document.body).on('touchstart', function(){
11996                 this.unmask();
11997             }, this);
11998             
11999             this.isApplied = true
12000         },
12001         
12002         mask : function(form, target)
12003         {
12004             this.form = form;
12005             
12006             this.target = target;
12007             
12008             if(!this.form.errorMask || !target.el){
12009                 return;
12010             }
12011             
12012             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12013             
12014             Roo.log(scrollable);
12015             
12016             var ot = this.target.el.calcOffsetsTo(scrollable);
12017             
12018             var scrollTo = ot[1] - this.form.maskOffset;
12019             
12020             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12021             
12022             scrollable.scrollTo('top', scrollTo);
12023             
12024             var box = this.target.el.getBox();
12025             Roo.log(box);
12026             var zIndex = Roo.bootstrap.Modal.zIndex++;
12027
12028             
12029             this.maskEl.top.setStyle('position', 'absolute');
12030             this.maskEl.top.setStyle('z-index', zIndex);
12031             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12032             this.maskEl.top.setLeft(0);
12033             this.maskEl.top.setTop(0);
12034             this.maskEl.top.show();
12035             
12036             this.maskEl.left.setStyle('position', 'absolute');
12037             this.maskEl.left.setStyle('z-index', zIndex);
12038             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12039             this.maskEl.left.setLeft(0);
12040             this.maskEl.left.setTop(box.y - this.padding);
12041             this.maskEl.left.show();
12042
12043             this.maskEl.bottom.setStyle('position', 'absolute');
12044             this.maskEl.bottom.setStyle('z-index', zIndex);
12045             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12046             this.maskEl.bottom.setLeft(0);
12047             this.maskEl.bottom.setTop(box.bottom + this.padding);
12048             this.maskEl.bottom.show();
12049
12050             this.maskEl.right.setStyle('position', 'absolute');
12051             this.maskEl.right.setStyle('z-index', zIndex);
12052             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12053             this.maskEl.right.setLeft(box.right + this.padding);
12054             this.maskEl.right.setTop(box.y - this.padding);
12055             this.maskEl.right.show();
12056
12057             this.toolTip.bindEl = this.target.el;
12058
12059             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12060
12061             var tip = this.target.blankText;
12062
12063             if(this.target.getValue() !== '' ) {
12064                 
12065                 if (this.target.invalidText.length) {
12066                     tip = this.target.invalidText;
12067                 } else if (this.target.regexText.length){
12068                     tip = this.target.regexText;
12069                 }
12070             }
12071
12072             this.toolTip.show(tip);
12073
12074             this.intervalID = window.setInterval(function() {
12075                 Roo.bootstrap.form.Form.popover.unmask();
12076             }, 10000);
12077
12078             window.onwheel = function(){ return false;};
12079             
12080             (function(){ this.isMasked = true; }).defer(500, this);
12081             
12082         },
12083         
12084         unmask : function()
12085         {
12086             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12087                 return;
12088             }
12089             
12090             this.maskEl.top.setStyle('position', 'absolute');
12091             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12092             this.maskEl.top.hide();
12093
12094             this.maskEl.left.setStyle('position', 'absolute');
12095             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12096             this.maskEl.left.hide();
12097
12098             this.maskEl.bottom.setStyle('position', 'absolute');
12099             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12100             this.maskEl.bottom.hide();
12101
12102             this.maskEl.right.setStyle('position', 'absolute');
12103             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12104             this.maskEl.right.hide();
12105             
12106             this.toolTip.hide();
12107             
12108             this.toolTip.el.hide();
12109             
12110             window.onwheel = function(){ return true;};
12111             
12112             if(this.intervalID){
12113                 window.clearInterval(this.intervalID);
12114                 this.intervalID = false;
12115             }
12116             
12117             this.isMasked = false;
12118             
12119         }
12120         
12121     }
12122     
12123 });
12124
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135 /**
12136  * @class Roo.form.VTypes
12137  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12138  * @static
12139  */
12140 Roo.form.VTypes = function(){
12141     // closure these in so they are only created once.
12142     var alpha = /^[a-zA-Z_]+$/;
12143     var alphanum = /^[a-zA-Z0-9_]+$/;
12144     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12145     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12146
12147     // All these messages and functions are configurable
12148     return {
12149         /**
12150          * The function used to validate email addresses
12151          * @param {String} value The email address
12152          */
12153         'email' : function(v){
12154             return email.test(v);
12155         },
12156         /**
12157          * The error text to display when the email validation function returns false
12158          * @type String
12159          */
12160         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12161         /**
12162          * The keystroke filter mask to be applied on email input
12163          * @type RegExp
12164          */
12165         'emailMask' : /[a-z0-9_\.\-@]/i,
12166
12167         /**
12168          * The function used to validate URLs
12169          * @param {String} value The URL
12170          */
12171         'url' : function(v){
12172             return url.test(v);
12173         },
12174         /**
12175          * The error text to display when the url validation function returns false
12176          * @type String
12177          */
12178         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12179         
12180         /**
12181          * The function used to validate alpha values
12182          * @param {String} value The value
12183          */
12184         'alpha' : function(v){
12185             return alpha.test(v);
12186         },
12187         /**
12188          * The error text to display when the alpha validation function returns false
12189          * @type String
12190          */
12191         'alphaText' : 'This field should only contain letters and _',
12192         /**
12193          * The keystroke filter mask to be applied on alpha input
12194          * @type RegExp
12195          */
12196         'alphaMask' : /[a-z_]/i,
12197
12198         /**
12199          * The function used to validate alphanumeric values
12200          * @param {String} value The value
12201          */
12202         'alphanum' : function(v){
12203             return alphanum.test(v);
12204         },
12205         /**
12206          * The error text to display when the alphanumeric validation function returns false
12207          * @type String
12208          */
12209         'alphanumText' : 'This field should only contain letters, numbers and _',
12210         /**
12211          * The keystroke filter mask to be applied on alphanumeric input
12212          * @type RegExp
12213          */
12214         'alphanumMask' : /[a-z0-9_]/i
12215     };
12216 }();/*
12217  * - LGPL
12218  *
12219  * Input
12220  * 
12221  */
12222
12223 /**
12224  * @class Roo.bootstrap.form.Input
12225  * @extends Roo.bootstrap.Component
12226  * Bootstrap Input class
12227  * @cfg {Boolean} disabled is it disabled
12228  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12229  * @cfg {String} name name of the input
12230  * @cfg {string} fieldLabel - the label associated
12231  * @cfg {string} placeholder - placeholder to put in text.
12232  * @cfg {string} before - input group add on before
12233  * @cfg {string} after - input group add on after
12234  * @cfg {string} size - (lg|sm) or leave empty..
12235  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12236  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12237  * @cfg {Number} md colspan out of 12 for computer-sized screens
12238  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12239  * @cfg {string} value default value of the input
12240  * @cfg {Number} labelWidth set the width of label 
12241  * @cfg {Number} labellg set the width of label (1-12)
12242  * @cfg {Number} labelmd set the width of label (1-12)
12243  * @cfg {Number} labelsm set the width of label (1-12)
12244  * @cfg {Number} labelxs set the width of label (1-12)
12245  * @cfg {String} labelAlign (top|left)
12246  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12247  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12248  * @cfg {String} indicatorpos (left|right) default left
12249  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12250  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12251  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12252  * @cfg {Roo.bootstrap.Button} before Button to show before
12253  * @cfg {Roo.bootstrap.Button} afterButton to show before
12254  * @cfg {String} align (left|center|right) Default left
12255  * @cfg {Boolean} forceFeedback (true|false) Default false
12256  * 
12257  * @constructor
12258  * Create a new Input
12259  * @param {Object} config The config object
12260  */
12261
12262 Roo.bootstrap.form.Input = function(config){
12263     
12264     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12265     
12266     this.addEvents({
12267         /**
12268          * @event focus
12269          * Fires when this field receives input focus.
12270          * @param {Roo.form.Field} this
12271          */
12272         focus : true,
12273         /**
12274          * @event blur
12275          * Fires when this field loses input focus.
12276          * @param {Roo.form.Field} this
12277          */
12278         blur : true,
12279         /**
12280          * @event specialkey
12281          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12282          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12283          * @param {Roo.form.Field} this
12284          * @param {Roo.EventObject} e The event object
12285          */
12286         specialkey : true,
12287         /**
12288          * @event change
12289          * Fires just before the field blurs if the field value has changed.
12290          * @param {Roo.form.Field} this
12291          * @param {Mixed} newValue The new value
12292          * @param {Mixed} oldValue The original value
12293          */
12294         change : true,
12295         /**
12296          * @event invalid
12297          * Fires after the field has been marked as invalid.
12298          * @param {Roo.form.Field} this
12299          * @param {String} msg The validation message
12300          */
12301         invalid : true,
12302         /**
12303          * @event valid
12304          * Fires after the field has been validated with no errors.
12305          * @param {Roo.form.Field} this
12306          */
12307         valid : true,
12308          /**
12309          * @event keyup
12310          * Fires after the key up
12311          * @param {Roo.form.Field} this
12312          * @param {Roo.EventObject}  e The event Object
12313          */
12314         keyup : true,
12315         /**
12316          * @event paste
12317          * Fires after the user pastes into input
12318          * @param {Roo.form.Field} this
12319          * @param {Roo.EventObject}  e The event Object
12320          */
12321         paste : true
12322     });
12323 };
12324
12325 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12326      /**
12327      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12328       automatic validation (defaults to "keyup").
12329      */
12330     validationEvent : "keyup",
12331      /**
12332      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12333      */
12334     validateOnBlur : true,
12335     /**
12336      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12337      */
12338     validationDelay : 250,
12339      /**
12340      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12341      */
12342     focusClass : "x-form-focus",  // not needed???
12343     
12344        
12345     /**
12346      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12347      */
12348     invalidClass : "has-warning",
12349     
12350     /**
12351      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12352      */
12353     validClass : "has-success",
12354     
12355     /**
12356      * @cfg {Boolean} hasFeedback (true|false) default true
12357      */
12358     hasFeedback : true,
12359     
12360     /**
12361      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12362      */
12363     invalidFeedbackClass : "glyphicon-warning-sign",
12364     
12365     /**
12366      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12367      */
12368     validFeedbackClass : "glyphicon-ok",
12369     
12370     /**
12371      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12372      */
12373     selectOnFocus : false,
12374     
12375      /**
12376      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12377      */
12378     maskRe : null,
12379        /**
12380      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12381      */
12382     vtype : null,
12383     
12384       /**
12385      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12386      */
12387     disableKeyFilter : false,
12388     
12389        /**
12390      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12391      */
12392     disabled : false,
12393      /**
12394      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12395      */
12396     allowBlank : true,
12397     /**
12398      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12399      */
12400     blankText : "Please complete this mandatory field",
12401     
12402      /**
12403      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12404      */
12405     minLength : 0,
12406     /**
12407      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12408      */
12409     maxLength : Number.MAX_VALUE,
12410     /**
12411      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12412      */
12413     minLengthText : "The minimum length for this field is {0}",
12414     /**
12415      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12416      */
12417     maxLengthText : "The maximum length for this field is {0}",
12418   
12419     
12420     /**
12421      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12422      * If available, this function will be called only after the basic validators all return true, and will be passed the
12423      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12424      */
12425     validator : null,
12426     /**
12427      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12428      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12429      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12430      */
12431     regex : null,
12432     /**
12433      * @cfg {String} regexText -- Depricated - use Invalid Text
12434      */
12435     regexText : "",
12436     
12437     /**
12438      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12439      */
12440     invalidText : "",
12441     
12442     
12443     
12444     autocomplete: false,
12445     
12446     
12447     fieldLabel : '',
12448     inputType : 'text',
12449     
12450     name : false,
12451     placeholder: false,
12452     before : false,
12453     after : false,
12454     size : false,
12455     hasFocus : false,
12456     preventMark: false,
12457     isFormField : true,
12458     value : '',
12459     labelWidth : 2,
12460     labelAlign : false,
12461     readOnly : false,
12462     align : false,
12463     formatedValue : false,
12464     forceFeedback : false,
12465     
12466     indicatorpos : 'left',
12467     
12468     labellg : 0,
12469     labelmd : 0,
12470     labelsm : 0,
12471     labelxs : 0,
12472     
12473     capture : '',
12474     accept : '',
12475     
12476     parentLabelAlign : function()
12477     {
12478         var parent = this;
12479         while (parent.parent()) {
12480             parent = parent.parent();
12481             if (typeof(parent.labelAlign) !='undefined') {
12482                 return parent.labelAlign;
12483             }
12484         }
12485         return 'left';
12486         
12487     },
12488     
12489     getAutoCreate : function()
12490     {
12491         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12492         
12493         var id = Roo.id();
12494         
12495         var cfg = {};
12496         
12497         if(this.inputType != 'hidden'){
12498             cfg.cls = 'form-group' //input-group
12499         }
12500         
12501         var input =  {
12502             tag: 'input',
12503             id : id,
12504             type : this.inputType,
12505             value : this.value,
12506             cls : 'form-control',
12507             placeholder : this.placeholder || '',
12508             autocomplete : this.autocomplete || 'new-password'
12509         };
12510         if (this.inputType == 'file') {
12511             input.style = 'overflow:hidden'; // why not in CSS?
12512         }
12513         
12514         if(this.capture.length){
12515             input.capture = this.capture;
12516         }
12517         
12518         if(this.accept.length){
12519             input.accept = this.accept + "/*";
12520         }
12521         
12522         if(this.align){
12523             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12524         }
12525         
12526         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12527             input.maxLength = this.maxLength;
12528         }
12529         
12530         if (this.disabled) {
12531             input.disabled=true;
12532         }
12533         
12534         if (this.readOnly) {
12535             input.readonly=true;
12536         }
12537         
12538         if (this.name) {
12539             input.name = this.name;
12540         }
12541         
12542         if (this.size) {
12543             input.cls += ' input-' + this.size;
12544         }
12545         
12546         var settings=this;
12547         ['xs','sm','md','lg'].map(function(size){
12548             if (settings[size]) {
12549                 cfg.cls += ' col-' + size + '-' + settings[size];
12550             }
12551         });
12552         
12553         var inputblock = input;
12554         
12555         var feedback = {
12556             tag: 'span',
12557             cls: 'glyphicon form-control-feedback'
12558         };
12559             
12560         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12561             
12562             inputblock = {
12563                 cls : 'has-feedback',
12564                 cn :  [
12565                     input,
12566                     feedback
12567                 ] 
12568             };  
12569         }
12570         
12571         if (this.before || this.after) {
12572             
12573             inputblock = {
12574                 cls : 'input-group',
12575                 cn :  [] 
12576             };
12577             
12578             if (this.before && typeof(this.before) == 'string') {
12579                 
12580                 inputblock.cn.push({
12581                     tag :'span',
12582                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12583                     html : this.before
12584                 });
12585             }
12586             if (this.before && typeof(this.before) == 'object') {
12587                 this.before = Roo.factory(this.before);
12588                 
12589                 inputblock.cn.push({
12590                     tag :'span',
12591                     cls : 'roo-input-before input-group-prepend   input-group-' +
12592                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12593                 });
12594             }
12595             
12596             inputblock.cn.push(input);
12597             
12598             if (this.after && typeof(this.after) == 'string') {
12599                 inputblock.cn.push({
12600                     tag :'span',
12601                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12602                     html : this.after
12603                 });
12604             }
12605             if (this.after && typeof(this.after) == 'object') {
12606                 this.after = Roo.factory(this.after);
12607                 
12608                 inputblock.cn.push({
12609                     tag :'span',
12610                     cls : 'roo-input-after input-group-append  input-group-' +
12611                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12612                 });
12613             }
12614             
12615             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12616                 inputblock.cls += ' has-feedback';
12617                 inputblock.cn.push(feedback);
12618             }
12619         };
12620         var indicator = {
12621             tag : 'i',
12622             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12623             tooltip : 'This field is required'
12624         };
12625         if (this.allowBlank ) {
12626             indicator.style = this.allowBlank ? ' display:none' : '';
12627         }
12628         if (align ==='left' && this.fieldLabel.length) {
12629             
12630             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12631             
12632             cfg.cn = [
12633                 indicator,
12634                 {
12635                     tag: 'label',
12636                     'for' :  id,
12637                     cls : 'control-label col-form-label',
12638                     html : this.fieldLabel
12639
12640                 },
12641                 {
12642                     cls : "", 
12643                     cn: [
12644                         inputblock
12645                     ]
12646                 }
12647             ];
12648             
12649             var labelCfg = cfg.cn[1];
12650             var contentCfg = cfg.cn[2];
12651             
12652             if(this.indicatorpos == 'right'){
12653                 cfg.cn = [
12654                     {
12655                         tag: 'label',
12656                         'for' :  id,
12657                         cls : 'control-label col-form-label',
12658                         cn : [
12659                             {
12660                                 tag : 'span',
12661                                 html : this.fieldLabel
12662                             },
12663                             indicator
12664                         ]
12665                     },
12666                     {
12667                         cls : "",
12668                         cn: [
12669                             inputblock
12670                         ]
12671                     }
12672
12673                 ];
12674                 
12675                 labelCfg = cfg.cn[0];
12676                 contentCfg = cfg.cn[1];
12677             
12678             }
12679             
12680             if(this.labelWidth > 12){
12681                 labelCfg.style = "width: " + this.labelWidth + 'px';
12682             }
12683             
12684             if(this.labelWidth < 13 && this.labelmd == 0){
12685                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12686             }
12687             
12688             if(this.labellg > 0){
12689                 labelCfg.cls += ' col-lg-' + this.labellg;
12690                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12691             }
12692             
12693             if(this.labelmd > 0){
12694                 labelCfg.cls += ' col-md-' + this.labelmd;
12695                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12696             }
12697             
12698             if(this.labelsm > 0){
12699                 labelCfg.cls += ' col-sm-' + this.labelsm;
12700                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12701             }
12702             
12703             if(this.labelxs > 0){
12704                 labelCfg.cls += ' col-xs-' + this.labelxs;
12705                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12706             }
12707             
12708             
12709         } else if ( this.fieldLabel.length) {
12710                 
12711             
12712             
12713             cfg.cn = [
12714                 {
12715                     tag : 'i',
12716                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12717                     tooltip : 'This field is required',
12718                     style : this.allowBlank ? ' display:none' : '' 
12719                 },
12720                 {
12721                     tag: 'label',
12722                    //cls : 'input-group-addon',
12723                     html : this.fieldLabel
12724
12725                 },
12726
12727                inputblock
12728
12729            ];
12730            
12731            if(this.indicatorpos == 'right'){
12732        
12733                 cfg.cn = [
12734                     {
12735                         tag: 'label',
12736                        //cls : 'input-group-addon',
12737                         html : this.fieldLabel
12738
12739                     },
12740                     {
12741                         tag : 'i',
12742                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12743                         tooltip : 'This field is required',
12744                         style : this.allowBlank ? ' display:none' : '' 
12745                     },
12746
12747                    inputblock
12748
12749                ];
12750
12751             }
12752
12753         } else {
12754             
12755             cfg.cn = [
12756
12757                     inputblock
12758
12759             ];
12760                 
12761                 
12762         };
12763         
12764         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12765            cfg.cls += ' navbar-form';
12766         }
12767         
12768         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12769             // on BS4 we do this only if not form 
12770             cfg.cls += ' navbar-form';
12771             cfg.tag = 'li';
12772         }
12773         
12774         return cfg;
12775         
12776     },
12777     /**
12778      * return the real input element.
12779      */
12780     inputEl: function ()
12781     {
12782         return this.el.select('input.form-control',true).first();
12783     },
12784     
12785     tooltipEl : function()
12786     {
12787         return this.inputEl();
12788     },
12789     
12790     indicatorEl : function()
12791     {
12792         if (Roo.bootstrap.version == 4) {
12793             return false; // not enabled in v4 yet.
12794         }
12795         
12796         var indicator = this.el.select('i.roo-required-indicator',true).first();
12797         
12798         if(!indicator){
12799             return false;
12800         }
12801         
12802         return indicator;
12803         
12804     },
12805     
12806     setDisabled : function(v)
12807     {
12808         var i  = this.inputEl().dom;
12809         if (!v) {
12810             i.removeAttribute('disabled');
12811             return;
12812             
12813         }
12814         i.setAttribute('disabled','true');
12815     },
12816     initEvents : function()
12817     {
12818           
12819         this.inputEl().on("keydown" , this.fireKey,  this);
12820         this.inputEl().on("focus", this.onFocus,  this);
12821         this.inputEl().on("blur", this.onBlur,  this);
12822         
12823         this.inputEl().relayEvent('keyup', this);
12824         this.inputEl().relayEvent('paste', this);
12825         
12826         this.indicator = this.indicatorEl();
12827         
12828         if(this.indicator){
12829             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12830         }
12831  
12832         // reference to original value for reset
12833         this.originalValue = this.getValue();
12834         //Roo.form.TextField.superclass.initEvents.call(this);
12835         if(this.validationEvent == 'keyup'){
12836             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12837             this.inputEl().on('keyup', this.filterValidation, this);
12838         }
12839         else if(this.validationEvent !== false){
12840             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12841         }
12842         
12843         if(this.selectOnFocus){
12844             this.on("focus", this.preFocus, this);
12845             
12846         }
12847         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12848             this.inputEl().on("keypress", this.filterKeys, this);
12849         } else {
12850             this.inputEl().relayEvent('keypress', this);
12851         }
12852        /* if(this.grow){
12853             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12854             this.el.on("click", this.autoSize,  this);
12855         }
12856         */
12857         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12858             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12859         }
12860         
12861         if (typeof(this.before) == 'object') {
12862             this.before.render(this.el.select('.roo-input-before',true).first());
12863         }
12864         if (typeof(this.after) == 'object') {
12865             this.after.render(this.el.select('.roo-input-after',true).first());
12866         }
12867         
12868         this.inputEl().on('change', this.onChange, this);
12869         
12870     },
12871     filterValidation : function(e){
12872         if(!e.isNavKeyPress()){
12873             this.validationTask.delay(this.validationDelay);
12874         }
12875     },
12876      /**
12877      * Validates the field value
12878      * @return {Boolean} True if the value is valid, else false
12879      */
12880     validate : function(){
12881         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12882         if(this.disabled || this.validateValue(this.getRawValue())){
12883             this.markValid();
12884             return true;
12885         }
12886         
12887         this.markInvalid();
12888         return false;
12889     },
12890     
12891     
12892     /**
12893      * Validates a value according to the field's validation rules and marks the field as invalid
12894      * if the validation fails
12895      * @param {Mixed} value The value to validate
12896      * @return {Boolean} True if the value is valid, else false
12897      */
12898     validateValue : function(value)
12899     {
12900         if(this.getVisibilityEl().hasClass('hidden')){
12901             return true;
12902         }
12903         
12904         if(value.length < 1)  { // if it's blank
12905             if(this.allowBlank){
12906                 return true;
12907             }
12908             return false;
12909         }
12910         
12911         if(value.length < this.minLength){
12912             return false;
12913         }
12914         if(value.length > this.maxLength){
12915             return false;
12916         }
12917         if(this.vtype){
12918             var vt = Roo.form.VTypes;
12919             if(!vt[this.vtype](value, this)){
12920                 return false;
12921             }
12922         }
12923         if(typeof this.validator == "function"){
12924             var msg = this.validator(value);
12925             if(msg !== true){
12926                 return false;
12927             }
12928             if (typeof(msg) == 'string') {
12929                 this.invalidText = msg;
12930             }
12931         }
12932         
12933         if(this.regex && !this.regex.test(value)){
12934             return false;
12935         }
12936         
12937         return true;
12938     },
12939     
12940      // private
12941     fireKey : function(e){
12942         //Roo.log('field ' + e.getKey());
12943         if(e.isNavKeyPress()){
12944             this.fireEvent("specialkey", this, e);
12945         }
12946     },
12947     focus : function (selectText){
12948         if(this.rendered){
12949             this.inputEl().focus();
12950             if(selectText === true){
12951                 this.inputEl().dom.select();
12952             }
12953         }
12954         return this;
12955     } ,
12956     
12957     onFocus : function(){
12958         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12959            // this.el.addClass(this.focusClass);
12960         }
12961         if(!this.hasFocus){
12962             this.hasFocus = true;
12963             this.startValue = this.getValue();
12964             this.fireEvent("focus", this);
12965         }
12966     },
12967     
12968     beforeBlur : Roo.emptyFn,
12969
12970     
12971     // private
12972     onBlur : function(){
12973         this.beforeBlur();
12974         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12975             //this.el.removeClass(this.focusClass);
12976         }
12977         this.hasFocus = false;
12978         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12979             this.validate();
12980         }
12981         var v = this.getValue();
12982         if(String(v) !== String(this.startValue)){
12983             this.fireEvent('change', this, v, this.startValue);
12984         }
12985         this.fireEvent("blur", this);
12986     },
12987     
12988     onChange : function(e)
12989     {
12990         var v = this.getValue();
12991         if(String(v) !== String(this.startValue)){
12992             this.fireEvent('change', this, v, this.startValue);
12993         }
12994         
12995     },
12996     
12997     /**
12998      * Resets the current field value to the originally loaded value and clears any validation messages
12999      */
13000     reset : function(){
13001         this.setValue(this.originalValue);
13002         this.validate();
13003     },
13004      /**
13005      * Returns the name of the field
13006      * @return {Mixed} name The name field
13007      */
13008     getName: function(){
13009         return this.name;
13010     },
13011      /**
13012      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13013      * @return {Mixed} value The field value
13014      */
13015     getValue : function(){
13016         
13017         var v = this.inputEl().getValue();
13018         
13019         return v;
13020     },
13021     /**
13022      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13023      * @return {Mixed} value The field value
13024      */
13025     getRawValue : function(){
13026         var v = this.inputEl().getValue();
13027         
13028         return v;
13029     },
13030     
13031     /**
13032      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13033      * @param {Mixed} value The value to set
13034      */
13035     setRawValue : function(v){
13036         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13037     },
13038     
13039     selectText : function(start, end){
13040         var v = this.getRawValue();
13041         if(v.length > 0){
13042             start = start === undefined ? 0 : start;
13043             end = end === undefined ? v.length : end;
13044             var d = this.inputEl().dom;
13045             if(d.setSelectionRange){
13046                 d.setSelectionRange(start, end);
13047             }else if(d.createTextRange){
13048                 var range = d.createTextRange();
13049                 range.moveStart("character", start);
13050                 range.moveEnd("character", v.length-end);
13051                 range.select();
13052             }
13053         }
13054     },
13055     
13056     /**
13057      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13058      * @param {Mixed} value The value to set
13059      */
13060     setValue : function(v){
13061         this.value = v;
13062         if(this.rendered){
13063             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13064             this.validate();
13065         }
13066     },
13067     
13068     /*
13069     processValue : function(value){
13070         if(this.stripCharsRe){
13071             var newValue = value.replace(this.stripCharsRe, '');
13072             if(newValue !== value){
13073                 this.setRawValue(newValue);
13074                 return newValue;
13075             }
13076         }
13077         return value;
13078     },
13079   */
13080     preFocus : function(){
13081         
13082         if(this.selectOnFocus){
13083             this.inputEl().dom.select();
13084         }
13085     },
13086     filterKeys : function(e){
13087         var k = e.getKey();
13088         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13089             return;
13090         }
13091         var c = e.getCharCode(), cc = String.fromCharCode(c);
13092         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13093             return;
13094         }
13095         if(!this.maskRe.test(cc)){
13096             e.stopEvent();
13097         }
13098     },
13099      /**
13100      * Clear any invalid styles/messages for this field
13101      */
13102     clearInvalid : function(){
13103         
13104         if(!this.el || this.preventMark){ // not rendered
13105             return;
13106         }
13107         
13108         
13109         this.el.removeClass([this.invalidClass, 'is-invalid']);
13110         
13111         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13112             
13113             var feedback = this.el.select('.form-control-feedback', true).first();
13114             
13115             if(feedback){
13116                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13117             }
13118             
13119         }
13120         
13121         if(this.indicator){
13122             this.indicator.removeClass('visible');
13123             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13124         }
13125         
13126         this.fireEvent('valid', this);
13127     },
13128     
13129      /**
13130      * Mark this field as valid
13131      */
13132     markValid : function()
13133     {
13134         if(!this.el  || this.preventMark){ // not rendered...
13135             return;
13136         }
13137         
13138         this.el.removeClass([this.invalidClass, this.validClass]);
13139         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13140
13141         var feedback = this.el.select('.form-control-feedback', true).first();
13142             
13143         if(feedback){
13144             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13145         }
13146         
13147         if(this.indicator){
13148             this.indicator.removeClass('visible');
13149             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13150         }
13151         
13152         if(this.disabled){
13153             return;
13154         }
13155         
13156            
13157         if(this.allowBlank && !this.getRawValue().length){
13158             return;
13159         }
13160         if (Roo.bootstrap.version == 3) {
13161             this.el.addClass(this.validClass);
13162         } else {
13163             this.inputEl().addClass('is-valid');
13164         }
13165
13166         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13167             
13168             var feedback = this.el.select('.form-control-feedback', true).first();
13169             
13170             if(feedback){
13171                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13172                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13173             }
13174             
13175         }
13176         
13177         this.fireEvent('valid', this);
13178     },
13179     
13180      /**
13181      * Mark this field as invalid
13182      * @param {String} msg The validation message
13183      */
13184     markInvalid : function(msg)
13185     {
13186         if(!this.el  || this.preventMark){ // not rendered
13187             return;
13188         }
13189         
13190         this.el.removeClass([this.invalidClass, this.validClass]);
13191         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13192         
13193         var feedback = this.el.select('.form-control-feedback', true).first();
13194             
13195         if(feedback){
13196             this.el.select('.form-control-feedback', true).first().removeClass(
13197                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13198         }
13199
13200         if(this.disabled){
13201             return;
13202         }
13203         
13204         if(this.allowBlank && !this.getRawValue().length){
13205             return;
13206         }
13207         
13208         if(this.indicator){
13209             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13210             this.indicator.addClass('visible');
13211         }
13212         if (Roo.bootstrap.version == 3) {
13213             this.el.addClass(this.invalidClass);
13214         } else {
13215             this.inputEl().addClass('is-invalid');
13216         }
13217         
13218         
13219         
13220         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13221             
13222             var feedback = this.el.select('.form-control-feedback', true).first();
13223             
13224             if(feedback){
13225                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13226                 
13227                 if(this.getValue().length || this.forceFeedback){
13228                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13229                 }
13230                 
13231             }
13232             
13233         }
13234         
13235         this.fireEvent('invalid', this, msg);
13236     },
13237     // private
13238     SafariOnKeyDown : function(event)
13239     {
13240         // this is a workaround for a password hang bug on chrome/ webkit.
13241         if (this.inputEl().dom.type != 'password') {
13242             return;
13243         }
13244         
13245         var isSelectAll = false;
13246         
13247         if(this.inputEl().dom.selectionEnd > 0){
13248             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13249         }
13250         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13251             event.preventDefault();
13252             this.setValue('');
13253             return;
13254         }
13255         
13256         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13257             
13258             event.preventDefault();
13259             // this is very hacky as keydown always get's upper case.
13260             //
13261             var cc = String.fromCharCode(event.getCharCode());
13262             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13263             
13264         }
13265     },
13266     adjustWidth : function(tag, w){
13267         tag = tag.toLowerCase();
13268         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13269             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13270                 if(tag == 'input'){
13271                     return w + 2;
13272                 }
13273                 if(tag == 'textarea'){
13274                     return w-2;
13275                 }
13276             }else if(Roo.isOpera){
13277                 if(tag == 'input'){
13278                     return w + 2;
13279                 }
13280                 if(tag == 'textarea'){
13281                     return w-2;
13282                 }
13283             }
13284         }
13285         return w;
13286     },
13287     
13288     setFieldLabel : function(v)
13289     {
13290         if(!this.rendered){
13291             return;
13292         }
13293         
13294         if(this.indicatorEl()){
13295             var ar = this.el.select('label > span',true);
13296             
13297             if (ar.elements.length) {
13298                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13299                 this.fieldLabel = v;
13300                 return;
13301             }
13302             
13303             var br = this.el.select('label',true);
13304             
13305             if(br.elements.length) {
13306                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13307                 this.fieldLabel = v;
13308                 return;
13309             }
13310             
13311             Roo.log('Cannot Found any of label > span || label in input');
13312             return;
13313         }
13314         
13315         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13316         this.fieldLabel = v;
13317         
13318         
13319     }
13320 });
13321
13322  
13323 /*
13324  * - LGPL
13325  *
13326  * Input
13327  * 
13328  */
13329
13330 /**
13331  * @class Roo.bootstrap.form.TextArea
13332  * @extends Roo.bootstrap.form.Input
13333  * Bootstrap TextArea class
13334  * @cfg {Number} cols Specifies the visible width of a text area
13335  * @cfg {Number} rows Specifies the visible number of lines in a text area
13336  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13337  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13338  * @cfg {string} html text
13339  * 
13340  * @constructor
13341  * Create a new TextArea
13342  * @param {Object} config The config object
13343  */
13344
13345 Roo.bootstrap.form.TextArea = function(config){
13346     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13347    
13348 };
13349
13350 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13351      
13352     cols : false,
13353     rows : 5,
13354     readOnly : false,
13355     warp : 'soft',
13356     resize : false,
13357     value: false,
13358     html: false,
13359     
13360     getAutoCreate : function(){
13361         
13362         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13363         
13364         var id = Roo.id();
13365         
13366         var cfg = {};
13367         
13368         if(this.inputType != 'hidden'){
13369             cfg.cls = 'form-group' //input-group
13370         }
13371         
13372         var input =  {
13373             tag: 'textarea',
13374             id : id,
13375             warp : this.warp,
13376             rows : this.rows,
13377             value : this.value || '',
13378             html: this.html || '',
13379             cls : 'form-control',
13380             placeholder : this.placeholder || '' 
13381             
13382         };
13383         
13384         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13385             input.maxLength = this.maxLength;
13386         }
13387         
13388         if(this.resize){
13389             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13390         }
13391         
13392         if(this.cols){
13393             input.cols = this.cols;
13394         }
13395         
13396         if (this.readOnly) {
13397             input.readonly = true;
13398         }
13399         
13400         if (this.name) {
13401             input.name = this.name;
13402         }
13403         
13404         if (this.size) {
13405             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13406         }
13407         
13408         var settings=this;
13409         ['xs','sm','md','lg'].map(function(size){
13410             if (settings[size]) {
13411                 cfg.cls += ' col-' + size + '-' + settings[size];
13412             }
13413         });
13414         
13415         var inputblock = input;
13416         
13417         if(this.hasFeedback && !this.allowBlank){
13418             
13419             var feedback = {
13420                 tag: 'span',
13421                 cls: 'glyphicon form-control-feedback'
13422             };
13423
13424             inputblock = {
13425                 cls : 'has-feedback',
13426                 cn :  [
13427                     input,
13428                     feedback
13429                 ] 
13430             };  
13431         }
13432         
13433         
13434         if (this.before || this.after) {
13435             
13436             inputblock = {
13437                 cls : 'input-group',
13438                 cn :  [] 
13439             };
13440             if (this.before) {
13441                 inputblock.cn.push({
13442                     tag :'span',
13443                     cls : 'input-group-addon',
13444                     html : this.before
13445                 });
13446             }
13447             
13448             inputblock.cn.push(input);
13449             
13450             if(this.hasFeedback && !this.allowBlank){
13451                 inputblock.cls += ' has-feedback';
13452                 inputblock.cn.push(feedback);
13453             }
13454             
13455             if (this.after) {
13456                 inputblock.cn.push({
13457                     tag :'span',
13458                     cls : 'input-group-addon',
13459                     html : this.after
13460                 });
13461             }
13462             
13463         }
13464         
13465         if (align ==='left' && this.fieldLabel.length) {
13466             cfg.cn = [
13467                 {
13468                     tag: 'label',
13469                     'for' :  id,
13470                     cls : 'control-label',
13471                     html : this.fieldLabel
13472                 },
13473                 {
13474                     cls : "",
13475                     cn: [
13476                         inputblock
13477                     ]
13478                 }
13479
13480             ];
13481             
13482             if(this.labelWidth > 12){
13483                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13484             }
13485
13486             if(this.labelWidth < 13 && this.labelmd == 0){
13487                 this.labelmd = this.labelWidth;
13488             }
13489
13490             if(this.labellg > 0){
13491                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13492                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13493             }
13494
13495             if(this.labelmd > 0){
13496                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13497                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13498             }
13499
13500             if(this.labelsm > 0){
13501                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13502                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13503             }
13504
13505             if(this.labelxs > 0){
13506                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13507                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13508             }
13509             
13510         } else if ( this.fieldLabel.length) {
13511             cfg.cn = [
13512
13513                {
13514                    tag: 'label',
13515                    //cls : 'input-group-addon',
13516                    html : this.fieldLabel
13517
13518                },
13519
13520                inputblock
13521
13522            ];
13523
13524         } else {
13525
13526             cfg.cn = [
13527
13528                 inputblock
13529
13530             ];
13531                 
13532         }
13533         
13534         if (this.disabled) {
13535             input.disabled=true;
13536         }
13537         
13538         return cfg;
13539         
13540     },
13541     /**
13542      * return the real textarea element.
13543      */
13544     inputEl: function ()
13545     {
13546         return this.el.select('textarea.form-control',true).first();
13547     },
13548     
13549     /**
13550      * Clear any invalid styles/messages for this field
13551      */
13552     clearInvalid : function()
13553     {
13554         
13555         if(!this.el || this.preventMark){ // not rendered
13556             return;
13557         }
13558         
13559         var label = this.el.select('label', true).first();
13560         var icon = this.el.select('i.fa-star', true).first();
13561         
13562         if(label && icon){
13563             icon.remove();
13564         }
13565         this.el.removeClass( this.validClass);
13566         this.inputEl().removeClass('is-invalid');
13567          
13568         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13569             
13570             var feedback = this.el.select('.form-control-feedback', true).first();
13571             
13572             if(feedback){
13573                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13574             }
13575             
13576         }
13577         
13578         this.fireEvent('valid', this);
13579     },
13580     
13581      /**
13582      * Mark this field as valid
13583      */
13584     markValid : function()
13585     {
13586         if(!this.el  || this.preventMark){ // not rendered
13587             return;
13588         }
13589         
13590         this.el.removeClass([this.invalidClass, this.validClass]);
13591         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13592         
13593         var feedback = this.el.select('.form-control-feedback', true).first();
13594             
13595         if(feedback){
13596             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13597         }
13598
13599         if(this.disabled || this.allowBlank){
13600             return;
13601         }
13602         
13603         var label = this.el.select('label', true).first();
13604         var icon = this.el.select('i.fa-star', true).first();
13605         
13606         if(label && icon){
13607             icon.remove();
13608         }
13609         if (Roo.bootstrap.version == 3) {
13610             this.el.addClass(this.validClass);
13611         } else {
13612             this.inputEl().addClass('is-valid');
13613         }
13614         
13615         
13616         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13617             
13618             var feedback = this.el.select('.form-control-feedback', true).first();
13619             
13620             if(feedback){
13621                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13622                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13623             }
13624             
13625         }
13626         
13627         this.fireEvent('valid', this);
13628     },
13629     
13630      /**
13631      * Mark this field as invalid
13632      * @param {String} msg The validation message
13633      */
13634     markInvalid : function(msg)
13635     {
13636         if(!this.el  || this.preventMark){ // not rendered
13637             return;
13638         }
13639         
13640         this.el.removeClass([this.invalidClass, this.validClass]);
13641         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13642         
13643         var feedback = this.el.select('.form-control-feedback', true).first();
13644             
13645         if(feedback){
13646             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13647         }
13648
13649         if(this.disabled || this.allowBlank){
13650             return;
13651         }
13652         
13653         var label = this.el.select('label', true).first();
13654         var icon = this.el.select('i.fa-star', true).first();
13655         
13656         if(!this.getValue().length && label && !icon){
13657             this.el.createChild({
13658                 tag : 'i',
13659                 cls : 'text-danger fa fa-lg fa-star',
13660                 tooltip : 'This field is required',
13661                 style : 'margin-right:5px;'
13662             }, label, true);
13663         }
13664         
13665         if (Roo.bootstrap.version == 3) {
13666             this.el.addClass(this.invalidClass);
13667         } else {
13668             this.inputEl().addClass('is-invalid');
13669         }
13670         
13671         // fixme ... this may be depricated need to test..
13672         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13673             
13674             var feedback = this.el.select('.form-control-feedback', true).first();
13675             
13676             if(feedback){
13677                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13678                 
13679                 if(this.getValue().length || this.forceFeedback){
13680                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13681                 }
13682                 
13683             }
13684             
13685         }
13686         
13687         this.fireEvent('invalid', this, msg);
13688     }
13689 });
13690
13691  
13692 /*
13693  * - LGPL
13694  *
13695  * trigger field - base class for combo..
13696  * 
13697  */
13698  
13699 /**
13700  * @class Roo.bootstrap.form.TriggerField
13701  * @extends Roo.bootstrap.form.Input
13702  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13703  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13704  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13705  * for which you can provide a custom implementation.  For example:
13706  * <pre><code>
13707 var trigger = new Roo.bootstrap.form.TriggerField();
13708 trigger.onTriggerClick = myTriggerFn;
13709 trigger.applyTo('my-field');
13710 </code></pre>
13711  *
13712  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13713  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13714  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13715  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13716  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13717
13718  * @constructor
13719  * Create a new TriggerField.
13720  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13721  * to the base TextField)
13722  */
13723 Roo.bootstrap.form.TriggerField = function(config){
13724     this.mimicing = false;
13725     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13726 };
13727
13728 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13729     /**
13730      * @cfg {String} triggerClass A CSS class to apply to the trigger
13731      */
13732      /**
13733      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13734      */
13735     hideTrigger:false,
13736
13737     /**
13738      * @cfg {Boolean} removable (true|false) special filter default false
13739      */
13740     removable : false,
13741     
13742     /** @cfg {Boolean} grow @hide */
13743     /** @cfg {Number} growMin @hide */
13744     /** @cfg {Number} growMax @hide */
13745
13746     /**
13747      * @hide 
13748      * @method
13749      */
13750     autoSize: Roo.emptyFn,
13751     // private
13752     monitorTab : true,
13753     // private
13754     deferHeight : true,
13755
13756     
13757     actionMode : 'wrap',
13758     
13759     caret : false,
13760     
13761     
13762     getAutoCreate : function(){
13763        
13764         var align = this.labelAlign || this.parentLabelAlign();
13765         
13766         var id = Roo.id();
13767         
13768         var cfg = {
13769             cls: 'form-group' //input-group
13770         };
13771         
13772         
13773         var input =  {
13774             tag: 'input',
13775             id : id,
13776             type : this.inputType,
13777             cls : 'form-control',
13778             autocomplete: 'new-password',
13779             placeholder : this.placeholder || '' 
13780             
13781         };
13782         if (this.name) {
13783             input.name = this.name;
13784         }
13785         if (this.size) {
13786             input.cls += ' input-' + this.size;
13787         }
13788         
13789         if (this.disabled) {
13790             input.disabled=true;
13791         }
13792         
13793         var inputblock = input;
13794         
13795         if(this.hasFeedback && !this.allowBlank){
13796             
13797             var feedback = {
13798                 tag: 'span',
13799                 cls: 'glyphicon form-control-feedback'
13800             };
13801             
13802             if(this.removable && !this.editable  ){
13803                 inputblock = {
13804                     cls : 'has-feedback',
13805                     cn :  [
13806                         inputblock,
13807                         {
13808                             tag: 'button',
13809                             html : 'x',
13810                             cls : 'roo-combo-removable-btn close'
13811                         },
13812                         feedback
13813                     ] 
13814                 };
13815             } else {
13816                 inputblock = {
13817                     cls : 'has-feedback',
13818                     cn :  [
13819                         inputblock,
13820                         feedback
13821                     ] 
13822                 };
13823             }
13824
13825         } else {
13826             if(this.removable && !this.editable ){
13827                 inputblock = {
13828                     cls : 'roo-removable',
13829                     cn :  [
13830                         inputblock,
13831                         {
13832                             tag: 'button',
13833                             html : 'x',
13834                             cls : 'roo-combo-removable-btn close'
13835                         }
13836                     ] 
13837                 };
13838             }
13839         }
13840         
13841         if (this.before || this.after) {
13842             
13843             inputblock = {
13844                 cls : 'input-group',
13845                 cn :  [] 
13846             };
13847             if (this.before) {
13848                 inputblock.cn.push({
13849                     tag :'span',
13850                     cls : 'input-group-addon input-group-prepend input-group-text',
13851                     html : this.before
13852                 });
13853             }
13854             
13855             inputblock.cn.push(input);
13856             
13857             if(this.hasFeedback && !this.allowBlank){
13858                 inputblock.cls += ' has-feedback';
13859                 inputblock.cn.push(feedback);
13860             }
13861             
13862             if (this.after) {
13863                 inputblock.cn.push({
13864                     tag :'span',
13865                     cls : 'input-group-addon input-group-append input-group-text',
13866                     html : this.after
13867                 });
13868             }
13869             
13870         };
13871         
13872       
13873         
13874         var ibwrap = inputblock;
13875         
13876         if(this.multiple){
13877             ibwrap = {
13878                 tag: 'ul',
13879                 cls: 'roo-select2-choices',
13880                 cn:[
13881                     {
13882                         tag: 'li',
13883                         cls: 'roo-select2-search-field',
13884                         cn: [
13885
13886                             inputblock
13887                         ]
13888                     }
13889                 ]
13890             };
13891                 
13892         }
13893         
13894         var combobox = {
13895             cls: 'roo-select2-container input-group',
13896             cn: [
13897                  {
13898                     tag: 'input',
13899                     type : 'hidden',
13900                     cls: 'form-hidden-field'
13901                 },
13902                 ibwrap
13903             ]
13904         };
13905         
13906         if(!this.multiple && this.showToggleBtn){
13907             
13908             var caret = {
13909                         tag: 'span',
13910                         cls: 'caret'
13911              };
13912             if (this.caret != false) {
13913                 caret = {
13914                      tag: 'i',
13915                      cls: 'fa fa-' + this.caret
13916                 };
13917                 
13918             }
13919             
13920             combobox.cn.push({
13921                 tag :'span',
13922                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13923                 cn : [
13924                     Roo.bootstrap.version == 3 ? caret : '',
13925                     {
13926                         tag: 'span',
13927                         cls: 'combobox-clear',
13928                         cn  : [
13929                             {
13930                                 tag : 'i',
13931                                 cls: 'icon-remove'
13932                             }
13933                         ]
13934                     }
13935                 ]
13936
13937             })
13938         }
13939         
13940         if(this.multiple){
13941             combobox.cls += ' roo-select2-container-multi';
13942         }
13943          var indicator = {
13944             tag : 'i',
13945             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13946             tooltip : 'This field is required'
13947         };
13948         if (Roo.bootstrap.version == 4) {
13949             indicator = {
13950                 tag : 'i',
13951                 style : 'display:none'
13952             };
13953         }
13954         
13955         
13956         if (align ==='left' && this.fieldLabel.length) {
13957             
13958             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13959
13960             cfg.cn = [
13961                 indicator,
13962                 {
13963                     tag: 'label',
13964                     'for' :  id,
13965                     cls : 'control-label',
13966                     html : this.fieldLabel
13967
13968                 },
13969                 {
13970                     cls : "", 
13971                     cn: [
13972                         combobox
13973                     ]
13974                 }
13975
13976             ];
13977             
13978             var labelCfg = cfg.cn[1];
13979             var contentCfg = cfg.cn[2];
13980             
13981             if(this.indicatorpos == 'right'){
13982                 cfg.cn = [
13983                     {
13984                         tag: 'label',
13985                         'for' :  id,
13986                         cls : 'control-label',
13987                         cn : [
13988                             {
13989                                 tag : 'span',
13990                                 html : this.fieldLabel
13991                             },
13992                             indicator
13993                         ]
13994                     },
13995                     {
13996                         cls : "", 
13997                         cn: [
13998                             combobox
13999                         ]
14000                     }
14001
14002                 ];
14003                 
14004                 labelCfg = cfg.cn[0];
14005                 contentCfg = cfg.cn[1];
14006             }
14007             
14008             if(this.labelWidth > 12){
14009                 labelCfg.style = "width: " + this.labelWidth + 'px';
14010             }
14011             
14012             if(this.labelWidth < 13 && this.labelmd == 0){
14013                 this.labelmd = this.labelWidth;
14014             }
14015             
14016             if(this.labellg > 0){
14017                 labelCfg.cls += ' col-lg-' + this.labellg;
14018                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14019             }
14020             
14021             if(this.labelmd > 0){
14022                 labelCfg.cls += ' col-md-' + this.labelmd;
14023                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14024             }
14025             
14026             if(this.labelsm > 0){
14027                 labelCfg.cls += ' col-sm-' + this.labelsm;
14028                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14029             }
14030             
14031             if(this.labelxs > 0){
14032                 labelCfg.cls += ' col-xs-' + this.labelxs;
14033                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14034             }
14035             
14036         } else if ( this.fieldLabel.length) {
14037 //                Roo.log(" label");
14038             cfg.cn = [
14039                 indicator,
14040                {
14041                    tag: 'label',
14042                    //cls : 'input-group-addon',
14043                    html : this.fieldLabel
14044
14045                },
14046
14047                combobox
14048
14049             ];
14050             
14051             if(this.indicatorpos == 'right'){
14052                 
14053                 cfg.cn = [
14054                     {
14055                        tag: 'label',
14056                        cn : [
14057                            {
14058                                tag : 'span',
14059                                html : this.fieldLabel
14060                            },
14061                            indicator
14062                        ]
14063
14064                     },
14065                     combobox
14066
14067                 ];
14068
14069             }
14070
14071         } else {
14072             
14073 //                Roo.log(" no label && no align");
14074                 cfg = combobox
14075                      
14076                 
14077         }
14078         
14079         var settings=this;
14080         ['xs','sm','md','lg'].map(function(size){
14081             if (settings[size]) {
14082                 cfg.cls += ' col-' + size + '-' + settings[size];
14083             }
14084         });
14085         
14086         return cfg;
14087         
14088     },
14089     
14090     
14091     
14092     // private
14093     onResize : function(w, h){
14094 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14095 //        if(typeof w == 'number'){
14096 //            var x = w - this.trigger.getWidth();
14097 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14098 //            this.trigger.setStyle('left', x+'px');
14099 //        }
14100     },
14101
14102     // private
14103     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14104
14105     // private
14106     getResizeEl : function(){
14107         return this.inputEl();
14108     },
14109
14110     // private
14111     getPositionEl : function(){
14112         return this.inputEl();
14113     },
14114
14115     // private
14116     alignErrorIcon : function(){
14117         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14118     },
14119
14120     // private
14121     initEvents : function(){
14122         
14123         this.createList();
14124         
14125         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14126         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14127         if(!this.multiple && this.showToggleBtn){
14128             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14129             if(this.hideTrigger){
14130                 this.trigger.setDisplayed(false);
14131             }
14132             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14133         }
14134         
14135         if(this.multiple){
14136             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14137         }
14138         
14139         if(this.removable && !this.editable && !this.tickable){
14140             var close = this.closeTriggerEl();
14141             
14142             if(close){
14143                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14144                 close.on('click', this.removeBtnClick, this, close);
14145             }
14146         }
14147         
14148         //this.trigger.addClassOnOver('x-form-trigger-over');
14149         //this.trigger.addClassOnClick('x-form-trigger-click');
14150         
14151         //if(!this.width){
14152         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14153         //}
14154     },
14155     
14156     closeTriggerEl : function()
14157     {
14158         var close = this.el.select('.roo-combo-removable-btn', true).first();
14159         return close ? close : false;
14160     },
14161     
14162     removeBtnClick : function(e, h, el)
14163     {
14164         e.preventDefault();
14165         
14166         if(this.fireEvent("remove", this) !== false){
14167             this.reset();
14168             this.fireEvent("afterremove", this)
14169         }
14170     },
14171     
14172     createList : function()
14173     {
14174         this.list = Roo.get(document.body).createChild({
14175             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14176             cls: 'typeahead typeahead-long dropdown-menu shadow',
14177             style: 'display:none'
14178         });
14179         
14180         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14181         
14182     },
14183
14184     // private
14185     initTrigger : function(){
14186        
14187     },
14188
14189     // private
14190     onDestroy : function(){
14191         if(this.trigger){
14192             this.trigger.removeAllListeners();
14193           //  this.trigger.remove();
14194         }
14195         //if(this.wrap){
14196         //    this.wrap.remove();
14197         //}
14198         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14199     },
14200
14201     // private
14202     onFocus : function(){
14203         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14204         /*
14205         if(!this.mimicing){
14206             this.wrap.addClass('x-trigger-wrap-focus');
14207             this.mimicing = true;
14208             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14209             if(this.monitorTab){
14210                 this.el.on("keydown", this.checkTab, this);
14211             }
14212         }
14213         */
14214     },
14215
14216     // private
14217     checkTab : function(e){
14218         if(e.getKey() == e.TAB){
14219             this.triggerBlur();
14220         }
14221     },
14222
14223     // private
14224     onBlur : function(){
14225         // do nothing
14226     },
14227
14228     // private
14229     mimicBlur : function(e, t){
14230         /*
14231         if(!this.wrap.contains(t) && this.validateBlur()){
14232             this.triggerBlur();
14233         }
14234         */
14235     },
14236
14237     // private
14238     triggerBlur : function(){
14239         this.mimicing = false;
14240         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14241         if(this.monitorTab){
14242             this.el.un("keydown", this.checkTab, this);
14243         }
14244         //this.wrap.removeClass('x-trigger-wrap-focus');
14245         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14246     },
14247
14248     // private
14249     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14250     validateBlur : function(e, t){
14251         return true;
14252     },
14253
14254     // private
14255     onDisable : function(){
14256         this.inputEl().dom.disabled = true;
14257         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14258         //if(this.wrap){
14259         //    this.wrap.addClass('x-item-disabled');
14260         //}
14261     },
14262
14263     // private
14264     onEnable : function(){
14265         this.inputEl().dom.disabled = false;
14266         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14267         //if(this.wrap){
14268         //    this.el.removeClass('x-item-disabled');
14269         //}
14270     },
14271
14272     // private
14273     onShow : function(){
14274         var ae = this.getActionEl();
14275         
14276         if(ae){
14277             ae.dom.style.display = '';
14278             ae.dom.style.visibility = 'visible';
14279         }
14280     },
14281
14282     // private
14283     
14284     onHide : function(){
14285         var ae = this.getActionEl();
14286         ae.dom.style.display = 'none';
14287     },
14288
14289     /**
14290      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14291      * by an implementing function.
14292      * @method
14293      * @param {EventObject} e
14294      */
14295     onTriggerClick : Roo.emptyFn
14296 });
14297  
14298 /*
14299 * Licence: LGPL
14300 */
14301
14302 /**
14303  * @class Roo.bootstrap.form.CardUploader
14304  * @extends Roo.bootstrap.Button
14305  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14306  * @cfg {Number} errorTimeout default 3000
14307  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14308  * @cfg {Array}  html The button text.
14309
14310  *
14311  * @constructor
14312  * Create a new CardUploader
14313  * @param {Object} config The config object
14314  */
14315
14316 Roo.bootstrap.form.CardUploader = function(config){
14317     
14318  
14319     
14320     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14321     
14322     
14323     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14324         return r.data.id
14325      });
14326     
14327      this.addEvents({
14328          // raw events
14329         /**
14330          * @event preview
14331          * When a image is clicked on - and needs to display a slideshow or similar..
14332          * @param {Roo.bootstrap.Card} this
14333          * @param {Object} The image information data 
14334          *
14335          */
14336         'preview' : true,
14337          /**
14338          * @event download
14339          * When a the download link is clicked
14340          * @param {Roo.bootstrap.Card} this
14341          * @param {Object} The image information data  contains 
14342          */
14343         'download' : true
14344         
14345     });
14346 };
14347  
14348 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14349     
14350      
14351     errorTimeout : 3000,
14352      
14353     images : false,
14354    
14355     fileCollection : false,
14356     allowBlank : true,
14357     
14358     getAutoCreate : function()
14359     {
14360         
14361         var cfg =  {
14362             cls :'form-group' ,
14363             cn : [
14364                
14365                 {
14366                     tag: 'label',
14367                    //cls : 'input-group-addon',
14368                     html : this.fieldLabel
14369
14370                 },
14371
14372                 {
14373                     tag: 'input',
14374                     type : 'hidden',
14375                     name : this.name,
14376                     value : this.value,
14377                     cls : 'd-none  form-control'
14378                 },
14379                 
14380                 {
14381                     tag: 'input',
14382                     multiple : 'multiple',
14383                     type : 'file',
14384                     cls : 'd-none  roo-card-upload-selector'
14385                 },
14386                 
14387                 {
14388                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14389                 },
14390                 {
14391                     cls : 'card-columns roo-card-uploader-container'
14392                 }
14393
14394             ]
14395         };
14396            
14397          
14398         return cfg;
14399     },
14400     
14401     getChildContainer : function() /// what children are added to.
14402     {
14403         return this.containerEl;
14404     },
14405    
14406     getButtonContainer : function() /// what children are added to.
14407     {
14408         return this.el.select(".roo-card-uploader-button-container").first();
14409     },
14410    
14411     initEvents : function()
14412     {
14413         
14414         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14415         
14416         var t = this;
14417         this.addxtype({
14418             xns: Roo.bootstrap,
14419
14420             xtype : 'Button',
14421             container_method : 'getButtonContainer' ,            
14422             html :  this.html, // fix changable?
14423             cls : 'w-100 ',
14424             listeners : {
14425                 'click' : function(btn, e) {
14426                     t.onClick(e);
14427                 }
14428             }
14429         });
14430         
14431         
14432         
14433         
14434         this.urlAPI = (window.createObjectURL && window) || 
14435                                 (window.URL && URL.revokeObjectURL && URL) || 
14436                                 (window.webkitURL && webkitURL);
14437                         
14438          
14439          
14440          
14441         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14442         
14443         this.selectorEl.on('change', this.onFileSelected, this);
14444         if (this.images) {
14445             var t = this;
14446             this.images.forEach(function(img) {
14447                 t.addCard(img)
14448             });
14449             this.images = false;
14450         }
14451         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14452          
14453        
14454     },
14455     
14456    
14457     onClick : function(e)
14458     {
14459         e.preventDefault();
14460          
14461         this.selectorEl.dom.click();
14462          
14463     },
14464     
14465     onFileSelected : function(e)
14466     {
14467         e.preventDefault();
14468         
14469         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14470             return;
14471         }
14472         
14473         Roo.each(this.selectorEl.dom.files, function(file){    
14474             this.addFile(file);
14475         }, this);
14476          
14477     },
14478     
14479       
14480     
14481       
14482     
14483     addFile : function(file)
14484     {
14485            
14486         if(typeof(file) === 'string'){
14487             throw "Add file by name?"; // should not happen
14488             return;
14489         }
14490         
14491         if(!file || !this.urlAPI){
14492             return;
14493         }
14494         
14495         // file;
14496         // file.type;
14497         
14498         var _this = this;
14499         
14500         
14501         var url = _this.urlAPI.createObjectURL( file);
14502            
14503         this.addCard({
14504             id : Roo.bootstrap.form.CardUploader.ID--,
14505             is_uploaded : false,
14506             src : url,
14507             srcfile : file,
14508             title : file.name,
14509             mimetype : file.type,
14510             preview : false,
14511             is_deleted : 0
14512         });
14513         
14514     },
14515     
14516     /**
14517      * addCard - add an Attachment to the uploader
14518      * @param data - the data about the image to upload
14519      *
14520      * {
14521           id : 123
14522           title : "Title of file",
14523           is_uploaded : false,
14524           src : "http://.....",
14525           srcfile : { the File upload object },
14526           mimetype : file.type,
14527           preview : false,
14528           is_deleted : 0
14529           .. any other data...
14530         }
14531      *
14532      * 
14533     */
14534     
14535     addCard : function (data)
14536     {
14537         // hidden input element?
14538         // if the file is not an image...
14539         //then we need to use something other that and header_image
14540         var t = this;
14541         //   remove.....
14542         var footer = [
14543             {
14544                 xns : Roo.bootstrap,
14545                 xtype : 'CardFooter',
14546                  items: [
14547                     {
14548                         xns : Roo.bootstrap,
14549                         xtype : 'Element',
14550                         cls : 'd-flex',
14551                         items : [
14552                             
14553                             {
14554                                 xns : Roo.bootstrap,
14555                                 xtype : 'Button',
14556                                 html : String.format("<small>{0}</small>", data.title),
14557                                 cls : 'col-10 text-left',
14558                                 size: 'sm',
14559                                 weight: 'link',
14560                                 fa : 'download',
14561                                 listeners : {
14562                                     click : function() {
14563                                      
14564                                         t.fireEvent( "download", t, data );
14565                                     }
14566                                 }
14567                             },
14568                           
14569                             {
14570                                 xns : Roo.bootstrap,
14571                                 xtype : 'Button',
14572                                 style: 'max-height: 28px; ',
14573                                 size : 'sm',
14574                                 weight: 'danger',
14575                                 cls : 'col-2',
14576                                 fa : 'times',
14577                                 listeners : {
14578                                     click : function() {
14579                                         t.removeCard(data.id)
14580                                     }
14581                                 }
14582                             }
14583                         ]
14584                     }
14585                     
14586                 ] 
14587             }
14588             
14589         ];
14590         
14591         var cn = this.addxtype(
14592             {
14593                  
14594                 xns : Roo.bootstrap,
14595                 xtype : 'Card',
14596                 closeable : true,
14597                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14598                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14599                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14600                 data : data,
14601                 html : false,
14602                  
14603                 items : footer,
14604                 initEvents : function() {
14605                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14606                     var card = this;
14607                     this.imgEl = this.el.select('.card-img-top').first();
14608                     if (this.imgEl) {
14609                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14610                         this.imgEl.set({ 'pointer' : 'cursor' });
14611                                   
14612                     }
14613                     this.getCardFooter().addClass('p-1');
14614                     
14615                   
14616                 }
14617                 
14618             }
14619         );
14620         // dont' really need ot update items.
14621         // this.items.push(cn);
14622         this.fileCollection.add(cn);
14623         
14624         if (!data.srcfile) {
14625             this.updateInput();
14626             return;
14627         }
14628             
14629         var _t = this;
14630         var reader = new FileReader();
14631         reader.addEventListener("load", function() {  
14632             data.srcdata =  reader.result;
14633             _t.updateInput();
14634         });
14635         reader.readAsDataURL(data.srcfile);
14636         
14637         
14638         
14639     },
14640     removeCard : function(id)
14641     {
14642         
14643         var card  = this.fileCollection.get(id);
14644         card.data.is_deleted = 1;
14645         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14646         //this.fileCollection.remove(card);
14647         //this.items = this.items.filter(function(e) { return e != card });
14648         // dont' really need ot update items.
14649         card.el.dom.parentNode.removeChild(card.el.dom);
14650         this.updateInput();
14651
14652         
14653     },
14654     reset: function()
14655     {
14656         this.fileCollection.each(function(card) {
14657             if (card.el.dom && card.el.dom.parentNode) {
14658                 card.el.dom.parentNode.removeChild(card.el.dom);
14659             }
14660         });
14661         this.fileCollection.clear();
14662         this.updateInput();
14663     },
14664     
14665     updateInput : function()
14666     {
14667          var data = [];
14668         this.fileCollection.each(function(e) {
14669             data.push(e.data);
14670             
14671         });
14672         this.inputEl().dom.value = JSON.stringify(data);
14673         
14674         
14675         
14676     }
14677     
14678     
14679 });
14680
14681
14682 Roo.bootstrap.form.CardUploader.ID = -1;/*
14683  * Based on:
14684  * Ext JS Library 1.1.1
14685  * Copyright(c) 2006-2007, Ext JS, LLC.
14686  *
14687  * Originally Released Under LGPL - original licence link has changed is not relivant.
14688  *
14689  * Fork - LGPL
14690  * <script type="text/javascript">
14691  */
14692
14693
14694 /**
14695  * @class Roo.data.SortTypes
14696  * @static
14697  * Defines the default sorting (casting?) comparison functions used when sorting data.
14698  */
14699 Roo.data.SortTypes = {
14700     /**
14701      * Default sort that does nothing
14702      * @param {Mixed} s The value being converted
14703      * @return {Mixed} The comparison value
14704      */
14705     none : function(s){
14706         return s;
14707     },
14708     
14709     /**
14710      * The regular expression used to strip tags
14711      * @type {RegExp}
14712      * @property
14713      */
14714     stripTagsRE : /<\/?[^>]+>/gi,
14715     
14716     /**
14717      * Strips all HTML tags to sort on text only
14718      * @param {Mixed} s The value being converted
14719      * @return {String} The comparison value
14720      */
14721     asText : function(s){
14722         return String(s).replace(this.stripTagsRE, "");
14723     },
14724     
14725     /**
14726      * Strips all HTML tags to sort on text only - Case insensitive
14727      * @param {Mixed} s The value being converted
14728      * @return {String} The comparison value
14729      */
14730     asUCText : function(s){
14731         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14732     },
14733     
14734     /**
14735      * Case insensitive string
14736      * @param {Mixed} s The value being converted
14737      * @return {String} The comparison value
14738      */
14739     asUCString : function(s) {
14740         return String(s).toUpperCase();
14741     },
14742     
14743     /**
14744      * Date sorting
14745      * @param {Mixed} s The value being converted
14746      * @return {Number} The comparison value
14747      */
14748     asDate : function(s) {
14749         if(!s){
14750             return 0;
14751         }
14752         if(s instanceof Date){
14753             return s.getTime();
14754         }
14755         return Date.parse(String(s));
14756     },
14757     
14758     /**
14759      * Float sorting
14760      * @param {Mixed} s The value being converted
14761      * @return {Float} The comparison value
14762      */
14763     asFloat : function(s) {
14764         var val = parseFloat(String(s).replace(/,/g, ""));
14765         if(isNaN(val)) {
14766             val = 0;
14767         }
14768         return val;
14769     },
14770     
14771     /**
14772      * Integer sorting
14773      * @param {Mixed} s The value being converted
14774      * @return {Number} The comparison value
14775      */
14776     asInt : function(s) {
14777         var val = parseInt(String(s).replace(/,/g, ""));
14778         if(isNaN(val)) {
14779             val = 0;
14780         }
14781         return val;
14782     }
14783 };/*
14784  * Based on:
14785  * Ext JS Library 1.1.1
14786  * Copyright(c) 2006-2007, Ext JS, LLC.
14787  *
14788  * Originally Released Under LGPL - original licence link has changed is not relivant.
14789  *
14790  * Fork - LGPL
14791  * <script type="text/javascript">
14792  */
14793
14794 /**
14795 * @class Roo.data.Record
14796  * Instances of this class encapsulate both record <em>definition</em> information, and record
14797  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14798  * to access Records cached in an {@link Roo.data.Store} object.<br>
14799  * <p>
14800  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14801  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14802  * objects.<br>
14803  * <p>
14804  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14805  * @constructor
14806  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14807  * {@link #create}. The parameters are the same.
14808  * @param {Array} data An associative Array of data values keyed by the field name.
14809  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14810  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14811  * not specified an integer id is generated.
14812  */
14813 Roo.data.Record = function(data, id){
14814     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14815     this.data = data;
14816 };
14817
14818 /**
14819  * Generate a constructor for a specific record layout.
14820  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14821  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14822  * Each field definition object may contain the following properties: <ul>
14823  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14824  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14825  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14826  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14827  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14828  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14829  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14830  * this may be omitted.</p></li>
14831  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14832  * <ul><li>auto (Default, implies no conversion)</li>
14833  * <li>string</li>
14834  * <li>int</li>
14835  * <li>float</li>
14836  * <li>boolean</li>
14837  * <li>date</li></ul></p></li>
14838  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14839  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14840  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14841  * by the Reader into an object that will be stored in the Record. It is passed the
14842  * following parameters:<ul>
14843  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14844  * </ul></p></li>
14845  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14846  * </ul>
14847  * <br>usage:<br><pre><code>
14848 var TopicRecord = Roo.data.Record.create(
14849     {name: 'title', mapping: 'topic_title'},
14850     {name: 'author', mapping: 'username'},
14851     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14852     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14853     {name: 'lastPoster', mapping: 'user2'},
14854     {name: 'excerpt', mapping: 'post_text'}
14855 );
14856
14857 var myNewRecord = new TopicRecord({
14858     title: 'Do my job please',
14859     author: 'noobie',
14860     totalPosts: 1,
14861     lastPost: new Date(),
14862     lastPoster: 'Animal',
14863     excerpt: 'No way dude!'
14864 });
14865 myStore.add(myNewRecord);
14866 </code></pre>
14867  * @method create
14868  * @static
14869  */
14870 Roo.data.Record.create = function(o){
14871     var f = function(){
14872         f.superclass.constructor.apply(this, arguments);
14873     };
14874     Roo.extend(f, Roo.data.Record);
14875     var p = f.prototype;
14876     p.fields = new Roo.util.MixedCollection(false, function(field){
14877         return field.name;
14878     });
14879     for(var i = 0, len = o.length; i < len; i++){
14880         p.fields.add(new Roo.data.Field(o[i]));
14881     }
14882     f.getField = function(name){
14883         return p.fields.get(name);  
14884     };
14885     return f;
14886 };
14887
14888 Roo.data.Record.AUTO_ID = 1000;
14889 Roo.data.Record.EDIT = 'edit';
14890 Roo.data.Record.REJECT = 'reject';
14891 Roo.data.Record.COMMIT = 'commit';
14892
14893 Roo.data.Record.prototype = {
14894     /**
14895      * Readonly flag - true if this record has been modified.
14896      * @type Boolean
14897      */
14898     dirty : false,
14899     editing : false,
14900     error: null,
14901     modified: null,
14902
14903     // private
14904     join : function(store){
14905         this.store = store;
14906     },
14907
14908     /**
14909      * Set the named field to the specified value.
14910      * @param {String} name The name of the field to set.
14911      * @param {Object} value The value to set the field to.
14912      */
14913     set : function(name, value){
14914         if(this.data[name] == value){
14915             return;
14916         }
14917         this.dirty = true;
14918         if(!this.modified){
14919             this.modified = {};
14920         }
14921         if(typeof this.modified[name] == 'undefined'){
14922             this.modified[name] = this.data[name];
14923         }
14924         this.data[name] = value;
14925         if(!this.editing && this.store){
14926             this.store.afterEdit(this);
14927         }       
14928     },
14929
14930     /**
14931      * Get the value of the named field.
14932      * @param {String} name The name of the field to get the value of.
14933      * @return {Object} The value of the field.
14934      */
14935     get : function(name){
14936         return this.data[name]; 
14937     },
14938
14939     // private
14940     beginEdit : function(){
14941         this.editing = true;
14942         this.modified = {}; 
14943     },
14944
14945     // private
14946     cancelEdit : function(){
14947         this.editing = false;
14948         delete this.modified;
14949     },
14950
14951     // private
14952     endEdit : function(){
14953         this.editing = false;
14954         if(this.dirty && this.store){
14955             this.store.afterEdit(this);
14956         }
14957     },
14958
14959     /**
14960      * Usually called by the {@link Roo.data.Store} which owns the Record.
14961      * Rejects all changes made to the Record since either creation, or the last commit operation.
14962      * Modified fields are reverted to their original values.
14963      * <p>
14964      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14965      * of reject operations.
14966      */
14967     reject : function(){
14968         var m = this.modified;
14969         for(var n in m){
14970             if(typeof m[n] != "function"){
14971                 this.data[n] = m[n];
14972             }
14973         }
14974         this.dirty = false;
14975         delete this.modified;
14976         this.editing = false;
14977         if(this.store){
14978             this.store.afterReject(this);
14979         }
14980     },
14981
14982     /**
14983      * Usually called by the {@link Roo.data.Store} which owns the Record.
14984      * Commits all changes made to the Record since either creation, or the last commit operation.
14985      * <p>
14986      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14987      * of commit operations.
14988      */
14989     commit : function(){
14990         this.dirty = false;
14991         delete this.modified;
14992         this.editing = false;
14993         if(this.store){
14994             this.store.afterCommit(this);
14995         }
14996     },
14997
14998     // private
14999     hasError : function(){
15000         return this.error != null;
15001     },
15002
15003     // private
15004     clearError : function(){
15005         this.error = null;
15006     },
15007
15008     /**
15009      * Creates a copy of this record.
15010      * @param {String} id (optional) A new record id if you don't want to use this record's id
15011      * @return {Record}
15012      */
15013     copy : function(newId) {
15014         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15015     }
15016 };/*
15017  * Based on:
15018  * Ext JS Library 1.1.1
15019  * Copyright(c) 2006-2007, Ext JS, LLC.
15020  *
15021  * Originally Released Under LGPL - original licence link has changed is not relivant.
15022  *
15023  * Fork - LGPL
15024  * <script type="text/javascript">
15025  */
15026
15027
15028
15029 /**
15030  * @class Roo.data.Store
15031  * @extends Roo.util.Observable
15032  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15033  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15034  * <p>
15035  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15036  * has no knowledge of the format of the data returned by the Proxy.<br>
15037  * <p>
15038  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15039  * instances from the data object. These records are cached and made available through accessor functions.
15040  * @constructor
15041  * Creates a new Store.
15042  * @param {Object} config A config object containing the objects needed for the Store to access data,
15043  * and read the data into Records.
15044  */
15045 Roo.data.Store = function(config){
15046     this.data = new Roo.util.MixedCollection(false);
15047     this.data.getKey = function(o){
15048         return o.id;
15049     };
15050     this.baseParams = {};
15051     // private
15052     this.paramNames = {
15053         "start" : "start",
15054         "limit" : "limit",
15055         "sort" : "sort",
15056         "dir" : "dir",
15057         "multisort" : "_multisort"
15058     };
15059
15060     if(config && config.data){
15061         this.inlineData = config.data;
15062         delete config.data;
15063     }
15064
15065     Roo.apply(this, config);
15066     
15067     if(this.reader){ // reader passed
15068         this.reader = Roo.factory(this.reader, Roo.data);
15069         this.reader.xmodule = this.xmodule || false;
15070         if(!this.recordType){
15071             this.recordType = this.reader.recordType;
15072         }
15073         if(this.reader.onMetaChange){
15074             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15075         }
15076     }
15077
15078     if(this.recordType){
15079         this.fields = this.recordType.prototype.fields;
15080     }
15081     this.modified = [];
15082
15083     this.addEvents({
15084         /**
15085          * @event datachanged
15086          * Fires when the data cache has changed, and a widget which is using this Store
15087          * as a Record cache should refresh its view.
15088          * @param {Store} this
15089          */
15090         datachanged : true,
15091         /**
15092          * @event metachange
15093          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15094          * @param {Store} this
15095          * @param {Object} meta The JSON metadata
15096          */
15097         metachange : true,
15098         /**
15099          * @event add
15100          * Fires when Records have been added to the Store
15101          * @param {Store} this
15102          * @param {Roo.data.Record[]} records The array of Records added
15103          * @param {Number} index The index at which the record(s) were added
15104          */
15105         add : true,
15106         /**
15107          * @event remove
15108          * Fires when a Record has been removed from the Store
15109          * @param {Store} this
15110          * @param {Roo.data.Record} record The Record that was removed
15111          * @param {Number} index The index at which the record was removed
15112          */
15113         remove : true,
15114         /**
15115          * @event update
15116          * Fires when a Record has been updated
15117          * @param {Store} this
15118          * @param {Roo.data.Record} record The Record that was updated
15119          * @param {String} operation The update operation being performed.  Value may be one of:
15120          * <pre><code>
15121  Roo.data.Record.EDIT
15122  Roo.data.Record.REJECT
15123  Roo.data.Record.COMMIT
15124          * </code></pre>
15125          */
15126         update : true,
15127         /**
15128          * @event clear
15129          * Fires when the data cache has been cleared.
15130          * @param {Store} this
15131          */
15132         clear : true,
15133         /**
15134          * @event beforeload
15135          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15136          * the load action will be canceled.
15137          * @param {Store} this
15138          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15139          */
15140         beforeload : true,
15141         /**
15142          * @event beforeloadadd
15143          * Fires after a new set of Records has been loaded.
15144          * @param {Store} this
15145          * @param {Roo.data.Record[]} records The Records that were loaded
15146          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15147          */
15148         beforeloadadd : true,
15149         /**
15150          * @event load
15151          * Fires after a new set of Records has been loaded, before they are added to the store.
15152          * @param {Store} this
15153          * @param {Roo.data.Record[]} records The Records that were loaded
15154          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15155          * @params {Object} return from reader
15156          */
15157         load : true,
15158         /**
15159          * @event loadexception
15160          * Fires if an exception occurs in the Proxy during loading.
15161          * Called with the signature of the Proxy's "loadexception" event.
15162          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15163          * 
15164          * @param {Proxy} 
15165          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15166          * @param {Object} load options 
15167          * @param {Object} jsonData from your request (normally this contains the Exception)
15168          */
15169         loadexception : true
15170     });
15171     
15172     if(this.proxy){
15173         this.proxy = Roo.factory(this.proxy, Roo.data);
15174         this.proxy.xmodule = this.xmodule || false;
15175         this.relayEvents(this.proxy,  ["loadexception"]);
15176     }
15177     this.sortToggle = {};
15178     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15179
15180     Roo.data.Store.superclass.constructor.call(this);
15181
15182     if(this.inlineData){
15183         this.loadData(this.inlineData);
15184         delete this.inlineData;
15185     }
15186 };
15187
15188 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15189      /**
15190     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15191     * without a remote query - used by combo/forms at present.
15192     */
15193     
15194     /**
15195     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15196     */
15197     /**
15198     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15199     */
15200     /**
15201     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15202     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15203     */
15204     /**
15205     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15206     * on any HTTP request
15207     */
15208     /**
15209     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15210     */
15211     /**
15212     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15213     */
15214     multiSort: false,
15215     /**
15216     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15217     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15218     */
15219     remoteSort : false,
15220
15221     /**
15222     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15223      * loaded or when a record is removed. (defaults to false).
15224     */
15225     pruneModifiedRecords : false,
15226
15227     // private
15228     lastOptions : null,
15229
15230     /**
15231      * Add Records to the Store and fires the add event.
15232      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15233      */
15234     add : function(records){
15235         records = [].concat(records);
15236         for(var i = 0, len = records.length; i < len; i++){
15237             records[i].join(this);
15238         }
15239         var index = this.data.length;
15240         this.data.addAll(records);
15241         this.fireEvent("add", this, records, index);
15242     },
15243
15244     /**
15245      * Remove a Record from the Store and fires the remove event.
15246      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15247      */
15248     remove : function(record){
15249         var index = this.data.indexOf(record);
15250         this.data.removeAt(index);
15251  
15252         if(this.pruneModifiedRecords){
15253             this.modified.remove(record);
15254         }
15255         this.fireEvent("remove", this, record, index);
15256     },
15257
15258     /**
15259      * Remove all Records from the Store and fires the clear event.
15260      */
15261     removeAll : function(){
15262         this.data.clear();
15263         if(this.pruneModifiedRecords){
15264             this.modified = [];
15265         }
15266         this.fireEvent("clear", this);
15267     },
15268
15269     /**
15270      * Inserts Records to the Store at the given index and fires the add event.
15271      * @param {Number} index The start index at which to insert the passed Records.
15272      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15273      */
15274     insert : function(index, records){
15275         records = [].concat(records);
15276         for(var i = 0, len = records.length; i < len; i++){
15277             this.data.insert(index, records[i]);
15278             records[i].join(this);
15279         }
15280         this.fireEvent("add", this, records, index);
15281     },
15282
15283     /**
15284      * Get the index within the cache of the passed Record.
15285      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15286      * @return {Number} The index of the passed Record. Returns -1 if not found.
15287      */
15288     indexOf : function(record){
15289         return this.data.indexOf(record);
15290     },
15291
15292     /**
15293      * Get the index within the cache of the Record with the passed id.
15294      * @param {String} id The id of the Record to find.
15295      * @return {Number} The index of the Record. Returns -1 if not found.
15296      */
15297     indexOfId : function(id){
15298         return this.data.indexOfKey(id);
15299     },
15300
15301     /**
15302      * Get the Record with the specified id.
15303      * @param {String} id The id of the Record to find.
15304      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15305      */
15306     getById : function(id){
15307         return this.data.key(id);
15308     },
15309
15310     /**
15311      * Get the Record at the specified index.
15312      * @param {Number} index The index of the Record to find.
15313      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15314      */
15315     getAt : function(index){
15316         return this.data.itemAt(index);
15317     },
15318
15319     /**
15320      * Returns a range of Records between specified indices.
15321      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15322      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15323      * @return {Roo.data.Record[]} An array of Records
15324      */
15325     getRange : function(start, end){
15326         return this.data.getRange(start, end);
15327     },
15328
15329     // private
15330     storeOptions : function(o){
15331         o = Roo.apply({}, o);
15332         delete o.callback;
15333         delete o.scope;
15334         this.lastOptions = o;
15335     },
15336
15337     /**
15338      * Loads the Record cache from the configured Proxy using the configured Reader.
15339      * <p>
15340      * If using remote paging, then the first load call must specify the <em>start</em>
15341      * and <em>limit</em> properties in the options.params property to establish the initial
15342      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15343      * <p>
15344      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15345      * and this call will return before the new data has been loaded. Perform any post-processing
15346      * in a callback function, or in a "load" event handler.</strong>
15347      * <p>
15348      * @param {Object} options An object containing properties which control loading options:<ul>
15349      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15350      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15351      * passed the following arguments:<ul>
15352      * <li>r : Roo.data.Record[]</li>
15353      * <li>options: Options object from the load call</li>
15354      * <li>success: Boolean success indicator</li></ul></li>
15355      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15356      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15357      * </ul>
15358      */
15359     load : function(options){
15360         options = options || {};
15361         if(this.fireEvent("beforeload", this, options) !== false){
15362             this.storeOptions(options);
15363             var p = Roo.apply(options.params || {}, this.baseParams);
15364             // if meta was not loaded from remote source.. try requesting it.
15365             if (!this.reader.metaFromRemote) {
15366                 p._requestMeta = 1;
15367             }
15368             if(this.sortInfo && this.remoteSort){
15369                 var pn = this.paramNames;
15370                 p[pn["sort"]] = this.sortInfo.field;
15371                 p[pn["dir"]] = this.sortInfo.direction;
15372             }
15373             if (this.multiSort) {
15374                 var pn = this.paramNames;
15375                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15376             }
15377             
15378             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15379         }
15380     },
15381
15382     /**
15383      * Reloads the Record cache from the configured Proxy using the configured Reader and
15384      * the options from the last load operation performed.
15385      * @param {Object} options (optional) An object containing properties which may override the options
15386      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15387      * the most recently used options are reused).
15388      */
15389     reload : function(options){
15390         this.load(Roo.applyIf(options||{}, this.lastOptions));
15391     },
15392
15393     // private
15394     // Called as a callback by the Reader during a load operation.
15395     loadRecords : function(o, options, success){
15396          
15397         if(!o){
15398             if(success !== false){
15399                 this.fireEvent("load", this, [], options, o);
15400             }
15401             if(options.callback){
15402                 options.callback.call(options.scope || this, [], options, false);
15403             }
15404             return;
15405         }
15406         // if data returned failure - throw an exception.
15407         if (o.success === false) {
15408             // show a message if no listener is registered.
15409             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15410                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15411             }
15412             // loadmask wil be hooked into this..
15413             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15414             return;
15415         }
15416         var r = o.records, t = o.totalRecords || r.length;
15417         
15418         this.fireEvent("beforeloadadd", this, r, options, o);
15419         
15420         if(!options || options.add !== true){
15421             if(this.pruneModifiedRecords){
15422                 this.modified = [];
15423             }
15424             for(var i = 0, len = r.length; i < len; i++){
15425                 r[i].join(this);
15426             }
15427             if(this.snapshot){
15428                 this.data = this.snapshot;
15429                 delete this.snapshot;
15430             }
15431             this.data.clear();
15432             this.data.addAll(r);
15433             this.totalLength = t;
15434             this.applySort();
15435             this.fireEvent("datachanged", this);
15436         }else{
15437             this.totalLength = Math.max(t, this.data.length+r.length);
15438             this.add(r);
15439         }
15440         
15441         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15442                 
15443             var e = new Roo.data.Record({});
15444
15445             e.set(this.parent.displayField, this.parent.emptyTitle);
15446             e.set(this.parent.valueField, '');
15447
15448             this.insert(0, e);
15449         }
15450             
15451         this.fireEvent("load", this, r, options, o);
15452         if(options.callback){
15453             options.callback.call(options.scope || this, r, options, true);
15454         }
15455     },
15456
15457
15458     /**
15459      * Loads data from a passed data block. A Reader which understands the format of the data
15460      * must have been configured in the constructor.
15461      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15462      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15463      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15464      */
15465     loadData : function(o, append){
15466         var r = this.reader.readRecords(o);
15467         this.loadRecords(r, {add: append}, true);
15468     },
15469     
15470      /**
15471      * using 'cn' the nested child reader read the child array into it's child stores.
15472      * @param {Object} rec The record with a 'children array
15473      */
15474     loadDataFromChildren : function(rec)
15475     {
15476         this.loadData(this.reader.toLoadData(rec));
15477     },
15478     
15479
15480     /**
15481      * Gets the number of cached records.
15482      * <p>
15483      * <em>If using paging, this may not be the total size of the dataset. If the data object
15484      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15485      * the data set size</em>
15486      */
15487     getCount : function(){
15488         return this.data.length || 0;
15489     },
15490
15491     /**
15492      * Gets the total number of records in the dataset as returned by the server.
15493      * <p>
15494      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15495      * the dataset size</em>
15496      */
15497     getTotalCount : function(){
15498         return this.totalLength || 0;
15499     },
15500
15501     /**
15502      * Returns the sort state of the Store as an object with two properties:
15503      * <pre><code>
15504  field {String} The name of the field by which the Records are sorted
15505  direction {String} The sort order, "ASC" or "DESC"
15506      * </code></pre>
15507      */
15508     getSortState : function(){
15509         return this.sortInfo;
15510     },
15511
15512     // private
15513     applySort : function(){
15514         if(this.sortInfo && !this.remoteSort){
15515             var s = this.sortInfo, f = s.field;
15516             var st = this.fields.get(f).sortType;
15517             var fn = function(r1, r2){
15518                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15519                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15520             };
15521             this.data.sort(s.direction, fn);
15522             if(this.snapshot && this.snapshot != this.data){
15523                 this.snapshot.sort(s.direction, fn);
15524             }
15525         }
15526     },
15527
15528     /**
15529      * Sets the default sort column and order to be used by the next load operation.
15530      * @param {String} fieldName The name of the field to sort by.
15531      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15532      */
15533     setDefaultSort : function(field, dir){
15534         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15535     },
15536
15537     /**
15538      * Sort the Records.
15539      * If remote sorting is used, the sort is performed on the server, and the cache is
15540      * reloaded. If local sorting is used, the cache is sorted internally.
15541      * @param {String} fieldName The name of the field to sort by.
15542      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15543      */
15544     sort : function(fieldName, dir){
15545         var f = this.fields.get(fieldName);
15546         if(!dir){
15547             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15548             
15549             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15550                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15551             }else{
15552                 dir = f.sortDir;
15553             }
15554         }
15555         this.sortToggle[f.name] = dir;
15556         this.sortInfo = {field: f.name, direction: dir};
15557         if(!this.remoteSort){
15558             this.applySort();
15559             this.fireEvent("datachanged", this);
15560         }else{
15561             this.load(this.lastOptions);
15562         }
15563     },
15564
15565     /**
15566      * Calls the specified function for each of the Records in the cache.
15567      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15568      * Returning <em>false</em> aborts and exits the iteration.
15569      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15570      */
15571     each : function(fn, scope){
15572         this.data.each(fn, scope);
15573     },
15574
15575     /**
15576      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15577      * (e.g., during paging).
15578      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15579      */
15580     getModifiedRecords : function(){
15581         return this.modified;
15582     },
15583
15584     // private
15585     createFilterFn : function(property, value, anyMatch){
15586         if(!value.exec){ // not a regex
15587             value = String(value);
15588             if(value.length == 0){
15589                 return false;
15590             }
15591             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15592         }
15593         return function(r){
15594             return value.test(r.data[property]);
15595         };
15596     },
15597
15598     /**
15599      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15600      * @param {String} property A field on your records
15601      * @param {Number} start The record index to start at (defaults to 0)
15602      * @param {Number} end The last record index to include (defaults to length - 1)
15603      * @return {Number} The sum
15604      */
15605     sum : function(property, start, end){
15606         var rs = this.data.items, v = 0;
15607         start = start || 0;
15608         end = (end || end === 0) ? end : rs.length-1;
15609
15610         for(var i = start; i <= end; i++){
15611             v += (rs[i].data[property] || 0);
15612         }
15613         return v;
15614     },
15615
15616     /**
15617      * Filter the records by a specified property.
15618      * @param {String} field A field on your records
15619      * @param {String/RegExp} value Either a string that the field
15620      * should start with or a RegExp to test against the field
15621      * @param {Boolean} anyMatch True to match any part not just the beginning
15622      */
15623     filter : function(property, value, anyMatch){
15624         var fn = this.createFilterFn(property, value, anyMatch);
15625         return fn ? this.filterBy(fn) : this.clearFilter();
15626     },
15627
15628     /**
15629      * Filter by a function. The specified function will be called with each
15630      * record in this data source. If the function returns true the record is included,
15631      * otherwise it is filtered.
15632      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15633      * @param {Object} scope (optional) The scope of the function (defaults to this)
15634      */
15635     filterBy : function(fn, scope){
15636         this.snapshot = this.snapshot || this.data;
15637         this.data = this.queryBy(fn, scope||this);
15638         this.fireEvent("datachanged", this);
15639     },
15640
15641     /**
15642      * Query the records by a specified property.
15643      * @param {String} field A field on your records
15644      * @param {String/RegExp} value Either a string that the field
15645      * should start with or a RegExp to test against the field
15646      * @param {Boolean} anyMatch True to match any part not just the beginning
15647      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15648      */
15649     query : function(property, value, anyMatch){
15650         var fn = this.createFilterFn(property, value, anyMatch);
15651         return fn ? this.queryBy(fn) : this.data.clone();
15652     },
15653
15654     /**
15655      * Query by a function. The specified function will be called with each
15656      * record in this data source. If the function returns true the record is included
15657      * in the results.
15658      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15659      * @param {Object} scope (optional) The scope of the function (defaults to this)
15660       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15661      **/
15662     queryBy : function(fn, scope){
15663         var data = this.snapshot || this.data;
15664         return data.filterBy(fn, scope||this);
15665     },
15666
15667     /**
15668      * Collects unique values for a particular dataIndex from this store.
15669      * @param {String} dataIndex The property to collect
15670      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15671      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15672      * @return {Array} An array of the unique values
15673      **/
15674     collect : function(dataIndex, allowNull, bypassFilter){
15675         var d = (bypassFilter === true && this.snapshot) ?
15676                 this.snapshot.items : this.data.items;
15677         var v, sv, r = [], l = {};
15678         for(var i = 0, len = d.length; i < len; i++){
15679             v = d[i].data[dataIndex];
15680             sv = String(v);
15681             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15682                 l[sv] = true;
15683                 r[r.length] = v;
15684             }
15685         }
15686         return r;
15687     },
15688
15689     /**
15690      * Revert to a view of the Record cache with no filtering applied.
15691      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15692      */
15693     clearFilter : function(suppressEvent){
15694         if(this.snapshot && this.snapshot != this.data){
15695             this.data = this.snapshot;
15696             delete this.snapshot;
15697             if(suppressEvent !== true){
15698                 this.fireEvent("datachanged", this);
15699             }
15700         }
15701     },
15702
15703     // private
15704     afterEdit : function(record){
15705         if(this.modified.indexOf(record) == -1){
15706             this.modified.push(record);
15707         }
15708         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15709     },
15710     
15711     // private
15712     afterReject : function(record){
15713         this.modified.remove(record);
15714         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15715     },
15716
15717     // private
15718     afterCommit : function(record){
15719         this.modified.remove(record);
15720         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15721     },
15722
15723     /**
15724      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15725      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15726      */
15727     commitChanges : function(){
15728         var m = this.modified.slice(0);
15729         this.modified = [];
15730         for(var i = 0, len = m.length; i < len; i++){
15731             m[i].commit();
15732         }
15733     },
15734
15735     /**
15736      * Cancel outstanding changes on all changed records.
15737      */
15738     rejectChanges : function(){
15739         var m = this.modified.slice(0);
15740         this.modified = [];
15741         for(var i = 0, len = m.length; i < len; i++){
15742             m[i].reject();
15743         }
15744     },
15745
15746     onMetaChange : function(meta, rtype, o){
15747         this.recordType = rtype;
15748         this.fields = rtype.prototype.fields;
15749         delete this.snapshot;
15750         this.sortInfo = meta.sortInfo || this.sortInfo;
15751         this.modified = [];
15752         this.fireEvent('metachange', this, this.reader.meta);
15753     },
15754     
15755     moveIndex : function(data, type)
15756     {
15757         var index = this.indexOf(data);
15758         
15759         var newIndex = index + type;
15760         
15761         this.remove(data);
15762         
15763         this.insert(newIndex, data);
15764         
15765     }
15766 });/*
15767  * Based on:
15768  * Ext JS Library 1.1.1
15769  * Copyright(c) 2006-2007, Ext JS, LLC.
15770  *
15771  * Originally Released Under LGPL - original licence link has changed is not relivant.
15772  *
15773  * Fork - LGPL
15774  * <script type="text/javascript">
15775  */
15776
15777 /**
15778  * @class Roo.data.SimpleStore
15779  * @extends Roo.data.Store
15780  * Small helper class to make creating Stores from Array data easier.
15781  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15782  * @cfg {Array} fields An array of field definition objects, or field name strings.
15783  * @cfg {Object} an existing reader (eg. copied from another store)
15784  * @cfg {Array} data The multi-dimensional array of data
15785  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15786  * @cfg {Roo.data.Reader} reader  [not-required] 
15787  * @constructor
15788  * @param {Object} config
15789  */
15790 Roo.data.SimpleStore = function(config)
15791 {
15792     Roo.data.SimpleStore.superclass.constructor.call(this, {
15793         isLocal : true,
15794         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15795                 id: config.id
15796             },
15797             Roo.data.Record.create(config.fields)
15798         ),
15799         proxy : new Roo.data.MemoryProxy(config.data)
15800     });
15801     this.load();
15802 };
15803 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15804  * Based on:
15805  * Ext JS Library 1.1.1
15806  * Copyright(c) 2006-2007, Ext JS, LLC.
15807  *
15808  * Originally Released Under LGPL - original licence link has changed is not relivant.
15809  *
15810  * Fork - LGPL
15811  * <script type="text/javascript">
15812  */
15813
15814 /**
15815 /**
15816  * @extends Roo.data.Store
15817  * @class Roo.data.JsonStore
15818  * Small helper class to make creating Stores for JSON data easier. <br/>
15819 <pre><code>
15820 var store = new Roo.data.JsonStore({
15821     url: 'get-images.php',
15822     root: 'images',
15823     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15824 });
15825 </code></pre>
15826  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15827  * JsonReader and HttpProxy (unless inline data is provided).</b>
15828  * @cfg {Array} fields An array of field definition objects, or field name strings.
15829  * @constructor
15830  * @param {Object} config
15831  */
15832 Roo.data.JsonStore = function(c){
15833     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15834         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15835         reader: new Roo.data.JsonReader(c, c.fields)
15836     }));
15837 };
15838 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15839  * Based on:
15840  * Ext JS Library 1.1.1
15841  * Copyright(c) 2006-2007, Ext JS, LLC.
15842  *
15843  * Originally Released Under LGPL - original licence link has changed is not relivant.
15844  *
15845  * Fork - LGPL
15846  * <script type="text/javascript">
15847  */
15848
15849  
15850 Roo.data.Field = function(config){
15851     if(typeof config == "string"){
15852         config = {name: config};
15853     }
15854     Roo.apply(this, config);
15855     
15856     if(!this.type){
15857         this.type = "auto";
15858     }
15859     
15860     var st = Roo.data.SortTypes;
15861     // named sortTypes are supported, here we look them up
15862     if(typeof this.sortType == "string"){
15863         this.sortType = st[this.sortType];
15864     }
15865     
15866     // set default sortType for strings and dates
15867     if(!this.sortType){
15868         switch(this.type){
15869             case "string":
15870                 this.sortType = st.asUCString;
15871                 break;
15872             case "date":
15873                 this.sortType = st.asDate;
15874                 break;
15875             default:
15876                 this.sortType = st.none;
15877         }
15878     }
15879
15880     // define once
15881     var stripRe = /[\$,%]/g;
15882
15883     // prebuilt conversion function for this field, instead of
15884     // switching every time we're reading a value
15885     if(!this.convert){
15886         var cv, dateFormat = this.dateFormat;
15887         switch(this.type){
15888             case "":
15889             case "auto":
15890             case undefined:
15891                 cv = function(v){ return v; };
15892                 break;
15893             case "string":
15894                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15895                 break;
15896             case "int":
15897                 cv = function(v){
15898                     return v !== undefined && v !== null && v !== '' ?
15899                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15900                     };
15901                 break;
15902             case "float":
15903                 cv = function(v){
15904                     return v !== undefined && v !== null && v !== '' ?
15905                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15906                     };
15907                 break;
15908             case "bool":
15909             case "boolean":
15910                 cv = function(v){ return v === true || v === "true" || v == 1; };
15911                 break;
15912             case "date":
15913                 cv = function(v){
15914                     if(!v){
15915                         return '';
15916                     }
15917                     if(v instanceof Date){
15918                         return v;
15919                     }
15920                     if(dateFormat){
15921                         if(dateFormat == "timestamp"){
15922                             return new Date(v*1000);
15923                         }
15924                         return Date.parseDate(v, dateFormat);
15925                     }
15926                     var parsed = Date.parse(v);
15927                     return parsed ? new Date(parsed) : null;
15928                 };
15929              break;
15930             
15931         }
15932         this.convert = cv;
15933     }
15934 };
15935
15936 Roo.data.Field.prototype = {
15937     dateFormat: null,
15938     defaultValue: "",
15939     mapping: null,
15940     sortType : null,
15941     sortDir : "ASC"
15942 };/*
15943  * Based on:
15944  * Ext JS Library 1.1.1
15945  * Copyright(c) 2006-2007, Ext JS, LLC.
15946  *
15947  * Originally Released Under LGPL - original licence link has changed is not relivant.
15948  *
15949  * Fork - LGPL
15950  * <script type="text/javascript">
15951  */
15952  
15953 // Base class for reading structured data from a data source.  This class is intended to be
15954 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15955
15956 /**
15957  * @class Roo.data.DataReader
15958  * @abstract
15959  * Base class for reading structured data from a data source.  This class is intended to be
15960  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15961  */
15962
15963 Roo.data.DataReader = function(meta, recordType){
15964     
15965     this.meta = meta;
15966     
15967     this.recordType = recordType instanceof Array ? 
15968         Roo.data.Record.create(recordType) : recordType;
15969 };
15970
15971 Roo.data.DataReader.prototype = {
15972     
15973     
15974     readerType : 'Data',
15975      /**
15976      * Create an empty record
15977      * @param {Object} data (optional) - overlay some values
15978      * @return {Roo.data.Record} record created.
15979      */
15980     newRow :  function(d) {
15981         var da =  {};
15982         this.recordType.prototype.fields.each(function(c) {
15983             switch( c.type) {
15984                 case 'int' : da[c.name] = 0; break;
15985                 case 'date' : da[c.name] = new Date(); break;
15986                 case 'float' : da[c.name] = 0.0; break;
15987                 case 'boolean' : da[c.name] = false; break;
15988                 default : da[c.name] = ""; break;
15989             }
15990             
15991         });
15992         return new this.recordType(Roo.apply(da, d));
15993     }
15994     
15995     
15996 };/*
15997  * Based on:
15998  * Ext JS Library 1.1.1
15999  * Copyright(c) 2006-2007, Ext JS, LLC.
16000  *
16001  * Originally Released Under LGPL - original licence link has changed is not relivant.
16002  *
16003  * Fork - LGPL
16004  * <script type="text/javascript">
16005  */
16006
16007 /**
16008  * @class Roo.data.DataProxy
16009  * @extends Roo.util.Observable
16010  * @abstract
16011  * This class is an abstract base class for implementations which provide retrieval of
16012  * unformatted data objects.<br>
16013  * <p>
16014  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16015  * (of the appropriate type which knows how to parse the data object) to provide a block of
16016  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16017  * <p>
16018  * Custom implementations must implement the load method as described in
16019  * {@link Roo.data.HttpProxy#load}.
16020  */
16021 Roo.data.DataProxy = function(){
16022     this.addEvents({
16023         /**
16024          * @event beforeload
16025          * Fires before a network request is made to retrieve a data object.
16026          * @param {Object} This DataProxy object.
16027          * @param {Object} params The params parameter to the load function.
16028          */
16029         beforeload : true,
16030         /**
16031          * @event load
16032          * Fires before the load method's callback is called.
16033          * @param {Object} This DataProxy object.
16034          * @param {Object} o The data object.
16035          * @param {Object} arg The callback argument object passed to the load function.
16036          */
16037         load : true,
16038         /**
16039          * @event loadexception
16040          * Fires if an Exception occurs during data retrieval.
16041          * @param {Object} This DataProxy object.
16042          * @param {Object} o The data object.
16043          * @param {Object} arg The callback argument object passed to the load function.
16044          * @param {Object} e The Exception.
16045          */
16046         loadexception : true
16047     });
16048     Roo.data.DataProxy.superclass.constructor.call(this);
16049 };
16050
16051 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16052
16053     /**
16054      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16055      */
16056 /*
16057  * Based on:
16058  * Ext JS Library 1.1.1
16059  * Copyright(c) 2006-2007, Ext JS, LLC.
16060  *
16061  * Originally Released Under LGPL - original licence link has changed is not relivant.
16062  *
16063  * Fork - LGPL
16064  * <script type="text/javascript">
16065  */
16066 /**
16067  * @class Roo.data.MemoryProxy
16068  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16069  * to the Reader when its load method is called.
16070  * @constructor
16071  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16072  */
16073 Roo.data.MemoryProxy = function(data){
16074     if (data.data) {
16075         data = data.data;
16076     }
16077     Roo.data.MemoryProxy.superclass.constructor.call(this);
16078     this.data = data;
16079 };
16080
16081 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16082     
16083     /**
16084      * Load data from the requested source (in this case an in-memory
16085      * data object passed to the constructor), read the data object into
16086      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16087      * process that block using the passed callback.
16088      * @param {Object} params This parameter is not used by the MemoryProxy class.
16089      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16090      * object into a block of Roo.data.Records.
16091      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16092      * The function must be passed <ul>
16093      * <li>The Record block object</li>
16094      * <li>The "arg" argument from the load function</li>
16095      * <li>A boolean success indicator</li>
16096      * </ul>
16097      * @param {Object} scope The scope in which to call the callback
16098      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16099      */
16100     load : function(params, reader, callback, scope, arg){
16101         params = params || {};
16102         var result;
16103         try {
16104             result = reader.readRecords(params.data ? params.data :this.data);
16105         }catch(e){
16106             this.fireEvent("loadexception", this, arg, null, e);
16107             callback.call(scope, null, arg, false);
16108             return;
16109         }
16110         callback.call(scope, result, arg, true);
16111     },
16112     
16113     // private
16114     update : function(params, records){
16115         
16116     }
16117 });/*
16118  * Based on:
16119  * Ext JS Library 1.1.1
16120  * Copyright(c) 2006-2007, Ext JS, LLC.
16121  *
16122  * Originally Released Under LGPL - original licence link has changed is not relivant.
16123  *
16124  * Fork - LGPL
16125  * <script type="text/javascript">
16126  */
16127 /**
16128  * @class Roo.data.HttpProxy
16129  * @extends Roo.data.DataProxy
16130  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16131  * configured to reference a certain URL.<br><br>
16132  * <p>
16133  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16134  * from which the running page was served.<br><br>
16135  * <p>
16136  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16137  * <p>
16138  * Be aware that to enable the browser to parse an XML document, the server must set
16139  * the Content-Type header in the HTTP response to "text/xml".
16140  * @constructor
16141  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16142  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16143  * will be used to make the request.
16144  */
16145 Roo.data.HttpProxy = function(conn){
16146     Roo.data.HttpProxy.superclass.constructor.call(this);
16147     // is conn a conn config or a real conn?
16148     this.conn = conn;
16149     this.useAjax = !conn || !conn.events;
16150   
16151 };
16152
16153 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16154     // thse are take from connection...
16155     
16156     /**
16157      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16158      */
16159     /**
16160      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16161      * extra parameters to each request made by this object. (defaults to undefined)
16162      */
16163     /**
16164      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16165      *  to each request made by this object. (defaults to undefined)
16166      */
16167     /**
16168      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16169      */
16170     /**
16171      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16172      */
16173      /**
16174      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16175      * @type Boolean
16176      */
16177   
16178
16179     /**
16180      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16181      * @type Boolean
16182      */
16183     /**
16184      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16185      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16186      * a finer-grained basis than the DataProxy events.
16187      */
16188     getConnection : function(){
16189         return this.useAjax ? Roo.Ajax : this.conn;
16190     },
16191
16192     /**
16193      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16194      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16195      * process that block using the passed callback.
16196      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16197      * for the request to the remote server.
16198      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16199      * object into a block of Roo.data.Records.
16200      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16201      * The function must be passed <ul>
16202      * <li>The Record block object</li>
16203      * <li>The "arg" argument from the load function</li>
16204      * <li>A boolean success indicator</li>
16205      * </ul>
16206      * @param {Object} scope The scope in which to call the callback
16207      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16208      */
16209     load : function(params, reader, callback, scope, arg){
16210         if(this.fireEvent("beforeload", this, params) !== false){
16211             var  o = {
16212                 params : params || {},
16213                 request: {
16214                     callback : callback,
16215                     scope : scope,
16216                     arg : arg
16217                 },
16218                 reader: reader,
16219                 callback : this.loadResponse,
16220                 scope: this
16221             };
16222             if(this.useAjax){
16223                 Roo.applyIf(o, this.conn);
16224                 if(this.activeRequest){
16225                     Roo.Ajax.abort(this.activeRequest);
16226                 }
16227                 this.activeRequest = Roo.Ajax.request(o);
16228             }else{
16229                 this.conn.request(o);
16230             }
16231         }else{
16232             callback.call(scope||this, null, arg, false);
16233         }
16234     },
16235
16236     // private
16237     loadResponse : function(o, success, response){
16238         delete this.activeRequest;
16239         if(!success){
16240             this.fireEvent("loadexception", this, o, response);
16241             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16242             return;
16243         }
16244         var result;
16245         try {
16246             result = o.reader.read(response);
16247         }catch(e){
16248             this.fireEvent("loadexception", this, o, response, e);
16249             o.request.callback.call(o.request.scope, {
16250                     success : false,
16251                     raw : {
16252                         errorMsg : response.responseText
16253                     }
16254                     
16255                 }, o.request.arg, false);
16256             return;
16257         }
16258         
16259         this.fireEvent("load", this, o, o.request.arg);
16260         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16261     },
16262
16263     // private
16264     update : function(dataSet){
16265
16266     },
16267
16268     // private
16269     updateResponse : function(dataSet){
16270
16271     }
16272 });/*
16273  * Based on:
16274  * Ext JS Library 1.1.1
16275  * Copyright(c) 2006-2007, Ext JS, LLC.
16276  *
16277  * Originally Released Under LGPL - original licence link has changed is not relivant.
16278  *
16279  * Fork - LGPL
16280  * <script type="text/javascript">
16281  */
16282
16283 /**
16284  * @class Roo.data.ScriptTagProxy
16285  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16286  * other than the originating domain of the running page.<br><br>
16287  * <p>
16288  * <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
16289  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16290  * <p>
16291  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16292  * source code that is used as the source inside a &lt;script> tag.<br><br>
16293  * <p>
16294  * In order for the browser to process the returned data, the server must wrap the data object
16295  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16296  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16297  * depending on whether the callback name was passed:
16298  * <p>
16299  * <pre><code>
16300 boolean scriptTag = false;
16301 String cb = request.getParameter("callback");
16302 if (cb != null) {
16303     scriptTag = true;
16304     response.setContentType("text/javascript");
16305 } else {
16306     response.setContentType("application/x-json");
16307 }
16308 Writer out = response.getWriter();
16309 if (scriptTag) {
16310     out.write(cb + "(");
16311 }
16312 out.print(dataBlock.toJsonString());
16313 if (scriptTag) {
16314     out.write(");");
16315 }
16316 </pre></code>
16317  *
16318  * @constructor
16319  * @param {Object} config A configuration object.
16320  */
16321 Roo.data.ScriptTagProxy = function(config){
16322     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16323     Roo.apply(this, config);
16324     this.head = document.getElementsByTagName("head")[0];
16325 };
16326
16327 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16328
16329 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16330     /**
16331      * @cfg {String} url The URL from which to request the data object.
16332      */
16333     /**
16334      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16335      */
16336     timeout : 30000,
16337     /**
16338      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16339      * the server the name of the callback function set up by the load call to process the returned data object.
16340      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16341      * javascript output which calls this named function passing the data object as its only parameter.
16342      */
16343     callbackParam : "callback",
16344     /**
16345      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16346      * name to the request.
16347      */
16348     nocache : true,
16349
16350     /**
16351      * Load data from the configured URL, read the data object into
16352      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16353      * process that block using the passed callback.
16354      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16355      * for the request to the remote server.
16356      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16357      * object into a block of Roo.data.Records.
16358      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16359      * The function must be passed <ul>
16360      * <li>The Record block object</li>
16361      * <li>The "arg" argument from the load function</li>
16362      * <li>A boolean success indicator</li>
16363      * </ul>
16364      * @param {Object} scope The scope in which to call the callback
16365      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16366      */
16367     load : function(params, reader, callback, scope, arg){
16368         if(this.fireEvent("beforeload", this, params) !== false){
16369
16370             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16371
16372             var url = this.url;
16373             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16374             if(this.nocache){
16375                 url += "&_dc=" + (new Date().getTime());
16376             }
16377             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16378             var trans = {
16379                 id : transId,
16380                 cb : "stcCallback"+transId,
16381                 scriptId : "stcScript"+transId,
16382                 params : params,
16383                 arg : arg,
16384                 url : url,
16385                 callback : callback,
16386                 scope : scope,
16387                 reader : reader
16388             };
16389             var conn = this;
16390
16391             window[trans.cb] = function(o){
16392                 conn.handleResponse(o, trans);
16393             };
16394
16395             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16396
16397             if(this.autoAbort !== false){
16398                 this.abort();
16399             }
16400
16401             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16402
16403             var script = document.createElement("script");
16404             script.setAttribute("src", url);
16405             script.setAttribute("type", "text/javascript");
16406             script.setAttribute("id", trans.scriptId);
16407             this.head.appendChild(script);
16408
16409             this.trans = trans;
16410         }else{
16411             callback.call(scope||this, null, arg, false);
16412         }
16413     },
16414
16415     // private
16416     isLoading : function(){
16417         return this.trans ? true : false;
16418     },
16419
16420     /**
16421      * Abort the current server request.
16422      */
16423     abort : function(){
16424         if(this.isLoading()){
16425             this.destroyTrans(this.trans);
16426         }
16427     },
16428
16429     // private
16430     destroyTrans : function(trans, isLoaded){
16431         this.head.removeChild(document.getElementById(trans.scriptId));
16432         clearTimeout(trans.timeoutId);
16433         if(isLoaded){
16434             window[trans.cb] = undefined;
16435             try{
16436                 delete window[trans.cb];
16437             }catch(e){}
16438         }else{
16439             // if hasn't been loaded, wait for load to remove it to prevent script error
16440             window[trans.cb] = function(){
16441                 window[trans.cb] = undefined;
16442                 try{
16443                     delete window[trans.cb];
16444                 }catch(e){}
16445             };
16446         }
16447     },
16448
16449     // private
16450     handleResponse : function(o, trans){
16451         this.trans = false;
16452         this.destroyTrans(trans, true);
16453         var result;
16454         try {
16455             result = trans.reader.readRecords(o);
16456         }catch(e){
16457             this.fireEvent("loadexception", this, o, trans.arg, e);
16458             trans.callback.call(trans.scope||window, null, trans.arg, false);
16459             return;
16460         }
16461         this.fireEvent("load", this, o, trans.arg);
16462         trans.callback.call(trans.scope||window, result, trans.arg, true);
16463     },
16464
16465     // private
16466     handleFailure : function(trans){
16467         this.trans = false;
16468         this.destroyTrans(trans, false);
16469         this.fireEvent("loadexception", this, null, trans.arg);
16470         trans.callback.call(trans.scope||window, null, trans.arg, false);
16471     }
16472 });/*
16473  * Based on:
16474  * Ext JS Library 1.1.1
16475  * Copyright(c) 2006-2007, Ext JS, LLC.
16476  *
16477  * Originally Released Under LGPL - original licence link has changed is not relivant.
16478  *
16479  * Fork - LGPL
16480  * <script type="text/javascript">
16481  */
16482
16483 /**
16484  * @class Roo.data.JsonReader
16485  * @extends Roo.data.DataReader
16486  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16487  * based on mappings in a provided Roo.data.Record constructor.
16488  * 
16489  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16490  * in the reply previously. 
16491  * 
16492  * <p>
16493  * Example code:
16494  * <pre><code>
16495 var RecordDef = Roo.data.Record.create([
16496     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16497     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16498 ]);
16499 var myReader = new Roo.data.JsonReader({
16500     totalProperty: "results",    // The property which contains the total dataset size (optional)
16501     root: "rows",                // The property which contains an Array of row objects
16502     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16503 }, RecordDef);
16504 </code></pre>
16505  * <p>
16506  * This would consume a JSON file like this:
16507  * <pre><code>
16508 { 'results': 2, 'rows': [
16509     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16510     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16511 }
16512 </code></pre>
16513  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16514  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16515  * paged from the remote server.
16516  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16517  * @cfg {String} root name of the property which contains the Array of row objects.
16518  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16519  * @cfg {Array} fields Array of field definition objects
16520  * @constructor
16521  * Create a new JsonReader
16522  * @param {Object} meta Metadata configuration options
16523  * @param {Object} recordType Either an Array of field definition objects,
16524  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16525  */
16526 Roo.data.JsonReader = function(meta, recordType){
16527     
16528     meta = meta || {};
16529     // set some defaults:
16530     Roo.applyIf(meta, {
16531         totalProperty: 'total',
16532         successProperty : 'success',
16533         root : 'data',
16534         id : 'id'
16535     });
16536     
16537     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16538 };
16539 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16540     
16541     readerType : 'Json',
16542     
16543     /**
16544      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16545      * Used by Store query builder to append _requestMeta to params.
16546      * 
16547      */
16548     metaFromRemote : false,
16549     /**
16550      * This method is only used by a DataProxy which has retrieved data from a remote server.
16551      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16552      * @return {Object} data A data block which is used by an Roo.data.Store object as
16553      * a cache of Roo.data.Records.
16554      */
16555     read : function(response){
16556         var json = response.responseText;
16557        
16558         var o = /* eval:var:o */ eval("("+json+")");
16559         if(!o) {
16560             throw {message: "JsonReader.read: Json object not found"};
16561         }
16562         
16563         if(o.metaData){
16564             
16565             delete this.ef;
16566             this.metaFromRemote = true;
16567             this.meta = o.metaData;
16568             this.recordType = Roo.data.Record.create(o.metaData.fields);
16569             this.onMetaChange(this.meta, this.recordType, o);
16570         }
16571         return this.readRecords(o);
16572     },
16573
16574     // private function a store will implement
16575     onMetaChange : function(meta, recordType, o){
16576
16577     },
16578
16579     /**
16580          * @ignore
16581          */
16582     simpleAccess: function(obj, subsc) {
16583         return obj[subsc];
16584     },
16585
16586         /**
16587          * @ignore
16588          */
16589     getJsonAccessor: function(){
16590         var re = /[\[\.]/;
16591         return function(expr) {
16592             try {
16593                 return(re.test(expr))
16594                     ? new Function("obj", "return obj." + expr)
16595                     : function(obj){
16596                         return obj[expr];
16597                     };
16598             } catch(e){}
16599             return Roo.emptyFn;
16600         };
16601     }(),
16602
16603     /**
16604      * Create a data block containing Roo.data.Records from an XML document.
16605      * @param {Object} o An object which contains an Array of row objects in the property specified
16606      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16607      * which contains the total size of the dataset.
16608      * @return {Object} data A data block which is used by an Roo.data.Store object as
16609      * a cache of Roo.data.Records.
16610      */
16611     readRecords : function(o){
16612         /**
16613          * After any data loads, the raw JSON data is available for further custom processing.
16614          * @type Object
16615          */
16616         this.o = o;
16617         var s = this.meta, Record = this.recordType,
16618             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16619
16620 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16621         if (!this.ef) {
16622             if(s.totalProperty) {
16623                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16624                 }
16625                 if(s.successProperty) {
16626                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16627                 }
16628                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16629                 if (s.id) {
16630                         var g = this.getJsonAccessor(s.id);
16631                         this.getId = function(rec) {
16632                                 var r = g(rec);  
16633                                 return (r === undefined || r === "") ? null : r;
16634                         };
16635                 } else {
16636                         this.getId = function(){return null;};
16637                 }
16638             this.ef = [];
16639             for(var jj = 0; jj < fl; jj++){
16640                 f = fi[jj];
16641                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16642                 this.ef[jj] = this.getJsonAccessor(map);
16643             }
16644         }
16645
16646         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16647         if(s.totalProperty){
16648             var vt = parseInt(this.getTotal(o), 10);
16649             if(!isNaN(vt)){
16650                 totalRecords = vt;
16651             }
16652         }
16653         if(s.successProperty){
16654             var vs = this.getSuccess(o);
16655             if(vs === false || vs === 'false'){
16656                 success = false;
16657             }
16658         }
16659         var records = [];
16660         for(var i = 0; i < c; i++){
16661                 var n = root[i];
16662             var values = {};
16663             var id = this.getId(n);
16664             for(var j = 0; j < fl; j++){
16665                 f = fi[j];
16666             var v = this.ef[j](n);
16667             if (!f.convert) {
16668                 Roo.log('missing convert for ' + f.name);
16669                 Roo.log(f);
16670                 continue;
16671             }
16672             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16673             }
16674             var record = new Record(values, id);
16675             record.json = n;
16676             records[i] = record;
16677         }
16678         return {
16679             raw : o,
16680             success : success,
16681             records : records,
16682             totalRecords : totalRecords
16683         };
16684     },
16685     // used when loading children.. @see loadDataFromChildren
16686     toLoadData: function(rec)
16687     {
16688         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16689         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16690         return { data : data, total : data.length };
16691         
16692     }
16693 });/*
16694  * Based on:
16695  * Ext JS Library 1.1.1
16696  * Copyright(c) 2006-2007, Ext JS, LLC.
16697  *
16698  * Originally Released Under LGPL - original licence link has changed is not relivant.
16699  *
16700  * Fork - LGPL
16701  * <script type="text/javascript">
16702  */
16703
16704 /**
16705  * @class Roo.data.ArrayReader
16706  * @extends Roo.data.DataReader
16707  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16708  * Each element of that Array represents a row of data fields. The
16709  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16710  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16711  * <p>
16712  * Example code:.
16713  * <pre><code>
16714 var RecordDef = Roo.data.Record.create([
16715     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16716     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16717 ]);
16718 var myReader = new Roo.data.ArrayReader({
16719     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16720 }, RecordDef);
16721 </code></pre>
16722  * <p>
16723  * This would consume an Array like this:
16724  * <pre><code>
16725 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16726   </code></pre>
16727  
16728  * @constructor
16729  * Create a new JsonReader
16730  * @param {Object} meta Metadata configuration options.
16731  * @param {Object|Array} recordType Either an Array of field definition objects
16732  * 
16733  * @cfg {Array} fields Array of field definition objects
16734  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16735  * as specified to {@link Roo.data.Record#create},
16736  * or an {@link Roo.data.Record} object
16737  *
16738  * 
16739  * created using {@link Roo.data.Record#create}.
16740  */
16741 Roo.data.ArrayReader = function(meta, recordType)
16742 {    
16743     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16744 };
16745
16746 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16747     
16748       /**
16749      * Create a data block containing Roo.data.Records from an XML document.
16750      * @param {Object} o An Array of row objects which represents the dataset.
16751      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16752      * a cache of Roo.data.Records.
16753      */
16754     readRecords : function(o)
16755     {
16756         var sid = this.meta ? this.meta.id : null;
16757         var recordType = this.recordType, fields = recordType.prototype.fields;
16758         var records = [];
16759         var root = o;
16760         for(var i = 0; i < root.length; i++){
16761             var n = root[i];
16762             var values = {};
16763             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16764             for(var j = 0, jlen = fields.length; j < jlen; j++){
16765                 var f = fields.items[j];
16766                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16767                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16768                 v = f.convert(v);
16769                 values[f.name] = v;
16770             }
16771             var record = new recordType(values, id);
16772             record.json = n;
16773             records[records.length] = record;
16774         }
16775         return {
16776             records : records,
16777             totalRecords : records.length
16778         };
16779     },
16780     // used when loading children.. @see loadDataFromChildren
16781     toLoadData: function(rec)
16782     {
16783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16784         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16785         
16786     }
16787     
16788     
16789 });/*
16790  * - LGPL
16791  * * 
16792  */
16793
16794 /**
16795  * @class Roo.bootstrap.form.ComboBox
16796  * @extends Roo.bootstrap.form.TriggerField
16797  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16798  * @cfg {Boolean} append (true|false) default false
16799  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16800  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16801  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16802  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16803  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16804  * @cfg {Boolean} animate default true
16805  * @cfg {Boolean} emptyResultText only for touch device
16806  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16807  * @cfg {String} emptyTitle default ''
16808  * @cfg {Number} width fixed with? experimental
16809  * @constructor
16810  * Create a new ComboBox.
16811  * @param {Object} config Configuration options
16812  */
16813 Roo.bootstrap.form.ComboBox = function(config){
16814     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16815     this.addEvents({
16816         /**
16817          * @event expand
16818          * Fires when the dropdown list is expanded
16819         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16820         */
16821         'expand' : true,
16822         /**
16823          * @event collapse
16824          * Fires when the dropdown list is collapsed
16825         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16826         */
16827         'collapse' : true,
16828         /**
16829          * @event beforeselect
16830          * Fires before a list item is selected. Return false to cancel the selection.
16831         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16832         * @param {Roo.data.Record} record The data record returned from the underlying store
16833         * @param {Number} index The index of the selected item in the dropdown list
16834         */
16835         'beforeselect' : true,
16836         /**
16837          * @event select
16838          * Fires when a list item is selected
16839         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16840         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16841         * @param {Number} index The index of the selected item in the dropdown list
16842         */
16843         'select' : true,
16844         /**
16845          * @event beforequery
16846          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16847          * The event object passed has these properties:
16848         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16849         * @param {String} query The query
16850         * @param {Boolean} forceAll true to force "all" query
16851         * @param {Boolean} cancel true to cancel the query
16852         * @param {Object} e The query event object
16853         */
16854         'beforequery': true,
16855          /**
16856          * @event add
16857          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16858         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859         */
16860         'add' : true,
16861         /**
16862          * @event edit
16863          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16864         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16865         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16866         */
16867         'edit' : true,
16868         /**
16869          * @event remove
16870          * Fires when the remove value from the combobox array
16871         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16872         */
16873         'remove' : true,
16874         /**
16875          * @event afterremove
16876          * Fires when the remove value from the combobox array
16877         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16878         */
16879         'afterremove' : true,
16880         /**
16881          * @event specialfilter
16882          * Fires when specialfilter
16883             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16884             */
16885         'specialfilter' : true,
16886         /**
16887          * @event tick
16888          * Fires when tick the element
16889             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16890             */
16891         'tick' : true,
16892         /**
16893          * @event touchviewdisplay
16894          * Fires when touch view require special display (default is using displayField)
16895             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896             * @param {Object} cfg set html .
16897             */
16898         'touchviewdisplay' : true
16899         
16900     });
16901     
16902     this.item = [];
16903     this.tickItems = [];
16904     
16905     this.selectedIndex = -1;
16906     if(this.mode == 'local'){
16907         if(config.queryDelay === undefined){
16908             this.queryDelay = 10;
16909         }
16910         if(config.minChars === undefined){
16911             this.minChars = 0;
16912         }
16913     }
16914 };
16915
16916 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16917      
16918     /**
16919      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16920      * rendering into an Roo.Editor, defaults to false)
16921      */
16922     /**
16923      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16924      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16925      */
16926     /**
16927      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16928      */
16929     /**
16930      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16931      * the dropdown list (defaults to undefined, with no header element)
16932      */
16933
16934      /**
16935      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16936      */
16937      
16938      /**
16939      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16940      */
16941     listWidth: undefined,
16942     /**
16943      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16944      * mode = 'remote' or 'text' if mode = 'local')
16945      */
16946     displayField: undefined,
16947     
16948     /**
16949      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16950      * mode = 'remote' or 'value' if mode = 'local'). 
16951      * Note: use of a valueField requires the user make a selection
16952      * in order for a value to be mapped.
16953      */
16954     valueField: undefined,
16955     /**
16956      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16957      */
16958     modalTitle : '',
16959     
16960     /**
16961      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16962      * field's data value (defaults to the underlying DOM element's name)
16963      */
16964     hiddenName: undefined,
16965     /**
16966      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16967      */
16968     listClass: '',
16969     /**
16970      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16971      */
16972     selectedClass: 'active',
16973     
16974     /**
16975      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16976      */
16977     shadow:'sides',
16978     /**
16979      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16980      * anchor positions (defaults to 'tl-bl')
16981      */
16982     listAlign: 'tl-bl?',
16983     /**
16984      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16985      */
16986     maxHeight: 300,
16987     /**
16988      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16989      * query specified by the allQuery config option (defaults to 'query')
16990      */
16991     triggerAction: 'query',
16992     /**
16993      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16994      * (defaults to 4, does not apply if editable = false)
16995      */
16996     minChars : 4,
16997     /**
16998      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16999      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17000      */
17001     typeAhead: false,
17002     /**
17003      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17004      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17005      */
17006     queryDelay: 500,
17007     /**
17008      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17009      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17010      */
17011     pageSize: 0,
17012     /**
17013      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17014      * when editable = true (defaults to false)
17015      */
17016     selectOnFocus:false,
17017     /**
17018      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17019      */
17020     queryParam: 'query',
17021     /**
17022      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17023      * when mode = 'remote' (defaults to 'Loading...')
17024      */
17025     loadingText: 'Loading...',
17026     /**
17027      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17028      */
17029     resizable: false,
17030     /**
17031      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17032      */
17033     handleHeight : 8,
17034     /**
17035      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17036      * traditional select (defaults to true)
17037      */
17038     editable: true,
17039     /**
17040      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17041      */
17042     allQuery: '',
17043     /**
17044      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17045      */
17046     mode: 'remote',
17047     /**
17048      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17049      * listWidth has a higher value)
17050      */
17051     minListWidth : 70,
17052     /**
17053      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17054      * allow the user to set arbitrary text into the field (defaults to false)
17055      */
17056     forceSelection:false,
17057     /**
17058      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17059      * if typeAhead = true (defaults to 250)
17060      */
17061     typeAheadDelay : 250,
17062     /**
17063      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17064      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17065      */
17066     valueNotFoundText : undefined,
17067     /**
17068      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17069      */
17070     blockFocus : false,
17071     
17072     /**
17073      * @cfg {Boolean} disableClear Disable showing of clear button.
17074      */
17075     disableClear : false,
17076     /**
17077      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17078      */
17079     alwaysQuery : false,
17080     
17081     /**
17082      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17083      */
17084     multiple : false,
17085     
17086     /**
17087      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17088      */
17089     invalidClass : "has-warning",
17090     
17091     /**
17092      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17093      */
17094     validClass : "has-success",
17095     
17096     /**
17097      * @cfg {Boolean} specialFilter (true|false) special filter default false
17098      */
17099     specialFilter : false,
17100     
17101     /**
17102      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17103      */
17104     mobileTouchView : true,
17105     
17106     /**
17107      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17108      */
17109     useNativeIOS : false,
17110     
17111     /**
17112      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17113      */
17114     mobile_restrict_height : false,
17115     
17116     ios_options : false,
17117     
17118     //private
17119     addicon : false,
17120     editicon: false,
17121     
17122     page: 0,
17123     hasQuery: false,
17124     append: false,
17125     loadNext: false,
17126     autoFocus : true,
17127     tickable : false,
17128     btnPosition : 'right',
17129     triggerList : true,
17130     showToggleBtn : true,
17131     animate : true,
17132     emptyResultText: 'Empty',
17133     triggerText : 'Select',
17134     emptyTitle : '',
17135     width : false,
17136     
17137     // element that contains real text value.. (when hidden is used..)
17138     
17139     getAutoCreate : function()
17140     {   
17141         var cfg = false;
17142         //render
17143         /*
17144          * Render classic select for iso
17145          */
17146         
17147         if(Roo.isIOS && this.useNativeIOS){
17148             cfg = this.getAutoCreateNativeIOS();
17149             return cfg;
17150         }
17151         
17152         /*
17153          * Touch Devices
17154          */
17155         
17156         if(Roo.isTouch && this.mobileTouchView){
17157             cfg = this.getAutoCreateTouchView();
17158             return cfg;;
17159         }
17160         
17161         /*
17162          *  Normal ComboBox
17163          */
17164         if(!this.tickable){
17165             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17166             return cfg;
17167         }
17168         
17169         /*
17170          *  ComboBox with tickable selections
17171          */
17172              
17173         var align = this.labelAlign || this.parentLabelAlign();
17174         
17175         cfg = {
17176             cls : 'form-group roo-combobox-tickable' //input-group
17177         };
17178         
17179         var btn_text_select = '';
17180         var btn_text_done = '';
17181         var btn_text_cancel = '';
17182         
17183         if (this.btn_text_show) {
17184             btn_text_select = 'Select';
17185             btn_text_done = 'Done';
17186             btn_text_cancel = 'Cancel'; 
17187         }
17188         
17189         var buttons = {
17190             tag : 'div',
17191             cls : 'tickable-buttons',
17192             cn : [
17193                 {
17194                     tag : 'button',
17195                     type : 'button',
17196                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17197                     //html : this.triggerText
17198                     html: btn_text_select
17199                 },
17200                 {
17201                     tag : 'button',
17202                     type : 'button',
17203                     name : 'ok',
17204                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17205                     //html : 'Done'
17206                     html: btn_text_done
17207                 },
17208                 {
17209                     tag : 'button',
17210                     type : 'button',
17211                     name : 'cancel',
17212                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17213                     //html : 'Cancel'
17214                     html: btn_text_cancel
17215                 }
17216             ]
17217         };
17218         
17219         if(this.editable){
17220             buttons.cn.unshift({
17221                 tag: 'input',
17222                 cls: 'roo-select2-search-field-input'
17223             });
17224         }
17225         
17226         var _this = this;
17227         
17228         Roo.each(buttons.cn, function(c){
17229             if (_this.size) {
17230                 c.cls += ' btn-' + _this.size;
17231             }
17232
17233             if (_this.disabled) {
17234                 c.disabled = true;
17235             }
17236         });
17237         
17238         var box = {
17239             tag: 'div',
17240             style : 'display: contents',
17241             cn: [
17242                 {
17243                     tag: 'input',
17244                     type : 'hidden',
17245                     cls: 'form-hidden-field'
17246                 },
17247                 {
17248                     tag: 'ul',
17249                     cls: 'roo-select2-choices',
17250                     cn:[
17251                         {
17252                             tag: 'li',
17253                             cls: 'roo-select2-search-field',
17254                             cn: [
17255                                 buttons
17256                             ]
17257                         }
17258                     ]
17259                 }
17260             ]
17261         };
17262         
17263         var combobox = {
17264             cls: 'roo-select2-container input-group roo-select2-container-multi',
17265             cn: [
17266                 
17267                 box
17268 //                {
17269 //                    tag: 'ul',
17270 //                    cls: 'typeahead typeahead-long dropdown-menu',
17271 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17272 //                }
17273             ]
17274         };
17275         
17276         if(this.hasFeedback && !this.allowBlank){
17277             
17278             var feedback = {
17279                 tag: 'span',
17280                 cls: 'glyphicon form-control-feedback'
17281             };
17282
17283             combobox.cn.push(feedback);
17284         }
17285         
17286         
17287         
17288         var indicator = {
17289             tag : 'i',
17290             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17291             tooltip : 'This field is required'
17292         };
17293         if (Roo.bootstrap.version == 4) {
17294             indicator = {
17295                 tag : 'i',
17296                 style : 'display:none'
17297             };
17298         }
17299         if (align ==='left' && this.fieldLabel.length) {
17300             
17301             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17302             
17303             cfg.cn = [
17304                 indicator,
17305                 {
17306                     tag: 'label',
17307                     'for' :  id,
17308                     cls : 'control-label col-form-label',
17309                     html : this.fieldLabel
17310
17311                 },
17312                 {
17313                     cls : "", 
17314                     cn: [
17315                         combobox
17316                     ]
17317                 }
17318
17319             ];
17320             
17321             var labelCfg = cfg.cn[1];
17322             var contentCfg = cfg.cn[2];
17323             
17324
17325             if(this.indicatorpos == 'right'){
17326                 
17327                 cfg.cn = [
17328                     {
17329                         tag: 'label',
17330                         'for' :  id,
17331                         cls : 'control-label col-form-label',
17332                         cn : [
17333                             {
17334                                 tag : 'span',
17335                                 html : this.fieldLabel
17336                             },
17337                             indicator
17338                         ]
17339                     },
17340                     {
17341                         cls : "",
17342                         cn: [
17343                             combobox
17344                         ]
17345                     }
17346
17347                 ];
17348                 
17349                 
17350                 
17351                 labelCfg = cfg.cn[0];
17352                 contentCfg = cfg.cn[1];
17353             
17354             }
17355             
17356             if(this.labelWidth > 12){
17357                 labelCfg.style = "width: " + this.labelWidth + 'px';
17358             }
17359             if(this.width * 1 > 0){
17360                 contentCfg.style = "width: " + this.width + 'px';
17361             }
17362             if(this.labelWidth < 13 && this.labelmd == 0){
17363                 this.labelmd = this.labelWidth;
17364             }
17365             
17366             if(this.labellg > 0){
17367                 labelCfg.cls += ' col-lg-' + this.labellg;
17368                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17369             }
17370             
17371             if(this.labelmd > 0){
17372                 labelCfg.cls += ' col-md-' + this.labelmd;
17373                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17374             }
17375             
17376             if(this.labelsm > 0){
17377                 labelCfg.cls += ' col-sm-' + this.labelsm;
17378                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17379             }
17380             
17381             if(this.labelxs > 0){
17382                 labelCfg.cls += ' col-xs-' + this.labelxs;
17383                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17384             }
17385                 
17386                 
17387         } else if ( this.fieldLabel.length) {
17388 //                Roo.log(" label");
17389                  cfg.cn = [
17390                    indicator,
17391                     {
17392                         tag: 'label',
17393                         //cls : 'input-group-addon',
17394                         html : this.fieldLabel
17395                     },
17396                     combobox
17397                 ];
17398                 
17399                 if(this.indicatorpos == 'right'){
17400                     cfg.cn = [
17401                         {
17402                             tag: 'label',
17403                             //cls : 'input-group-addon',
17404                             html : this.fieldLabel
17405                         },
17406                         indicator,
17407                         combobox
17408                     ];
17409                     
17410                 }
17411
17412         } else {
17413             
17414 //                Roo.log(" no label && no align");
17415                 cfg = combobox
17416                      
17417                 
17418         }
17419          
17420         var settings=this;
17421         ['xs','sm','md','lg'].map(function(size){
17422             if (settings[size]) {
17423                 cfg.cls += ' col-' + size + '-' + settings[size];
17424             }
17425         });
17426         
17427         return cfg;
17428         
17429     },
17430     
17431     _initEventsCalled : false,
17432     
17433     // private
17434     initEvents: function()
17435     {   
17436         if (this._initEventsCalled) { // as we call render... prevent looping...
17437             return;
17438         }
17439         this._initEventsCalled = true;
17440         
17441         if (!this.store) {
17442             throw "can not find store for combo";
17443         }
17444         
17445         this.indicator = this.indicatorEl();
17446         
17447         this.store = Roo.factory(this.store, Roo.data);
17448         this.store.parent = this;
17449         
17450         // if we are building from html. then this element is so complex, that we can not really
17451         // use the rendered HTML.
17452         // so we have to trash and replace the previous code.
17453         if (Roo.XComponent.build_from_html) {
17454             // remove this element....
17455             var e = this.el.dom, k=0;
17456             while (e ) { e = e.previousSibling;  ++k;}
17457
17458             this.el.remove();
17459             
17460             this.el=false;
17461             this.rendered = false;
17462             
17463             this.render(this.parent().getChildContainer(true), k);
17464         }
17465         
17466         if(Roo.isIOS && this.useNativeIOS){
17467             this.initIOSView();
17468             return;
17469         }
17470         
17471         /*
17472          * Touch Devices
17473          */
17474         
17475         if(Roo.isTouch && this.mobileTouchView){
17476             this.initTouchView();
17477             return;
17478         }
17479         
17480         if(this.tickable){
17481             this.initTickableEvents();
17482             return;
17483         }
17484         
17485         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17486         
17487         if(this.hiddenName){
17488             
17489             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17490             
17491             this.hiddenField.dom.value =
17492                 this.hiddenValue !== undefined ? this.hiddenValue :
17493                 this.value !== undefined ? this.value : '';
17494
17495             // prevent input submission
17496             this.el.dom.removeAttribute('name');
17497             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17498              
17499              
17500         }
17501         //if(Roo.isGecko){
17502         //    this.el.dom.setAttribute('autocomplete', 'off');
17503         //}
17504         
17505         var cls = 'x-combo-list';
17506         
17507         //this.list = new Roo.Layer({
17508         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17509         //});
17510         
17511         var _this = this;
17512         
17513         (function(){
17514             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17515             _this.list.setWidth(lw);
17516         }).defer(100);
17517         
17518         this.list.on('mouseover', this.onViewOver, this);
17519         this.list.on('mousemove', this.onViewMove, this);
17520         this.list.on('scroll', this.onViewScroll, this);
17521         
17522         /*
17523         this.list.swallowEvent('mousewheel');
17524         this.assetHeight = 0;
17525
17526         if(this.title){
17527             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17528             this.assetHeight += this.header.getHeight();
17529         }
17530
17531         this.innerList = this.list.createChild({cls:cls+'-inner'});
17532         this.innerList.on('mouseover', this.onViewOver, this);
17533         this.innerList.on('mousemove', this.onViewMove, this);
17534         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17535         
17536         if(this.allowBlank && !this.pageSize && !this.disableClear){
17537             this.footer = this.list.createChild({cls:cls+'-ft'});
17538             this.pageTb = new Roo.Toolbar(this.footer);
17539            
17540         }
17541         if(this.pageSize){
17542             this.footer = this.list.createChild({cls:cls+'-ft'});
17543             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17544                     {pageSize: this.pageSize});
17545             
17546         }
17547         
17548         if (this.pageTb && this.allowBlank && !this.disableClear) {
17549             var _this = this;
17550             this.pageTb.add(new Roo.Toolbar.Fill(), {
17551                 cls: 'x-btn-icon x-btn-clear',
17552                 text: '&#160;',
17553                 handler: function()
17554                 {
17555                     _this.collapse();
17556                     _this.clearValue();
17557                     _this.onSelect(false, -1);
17558                 }
17559             });
17560         }
17561         if (this.footer) {
17562             this.assetHeight += this.footer.getHeight();
17563         }
17564         */
17565             
17566         if(!this.tpl){
17567             this.tpl = Roo.bootstrap.version == 4 ?
17568                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17569                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17570         }
17571
17572         this.view = new Roo.View(this.list, this.tpl, {
17573             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17574         });
17575         //this.view.wrapEl.setDisplayed(false);
17576         this.view.on('click', this.onViewClick, this);
17577         
17578         
17579         this.store.on('beforeload', this.onBeforeLoad, this);
17580         this.store.on('load', this.onLoad, this);
17581         this.store.on('loadexception', this.onLoadException, this);
17582         /*
17583         if(this.resizable){
17584             this.resizer = new Roo.Resizable(this.list,  {
17585                pinned:true, handles:'se'
17586             });
17587             this.resizer.on('resize', function(r, w, h){
17588                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17589                 this.listWidth = w;
17590                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17591                 this.restrictHeight();
17592             }, this);
17593             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17594         }
17595         */
17596         if(!this.editable){
17597             this.editable = true;
17598             this.setEditable(false);
17599         }
17600         
17601         /*
17602         
17603         if (typeof(this.events.add.listeners) != 'undefined') {
17604             
17605             this.addicon = this.wrap.createChild(
17606                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17607        
17608             this.addicon.on('click', function(e) {
17609                 this.fireEvent('add', this);
17610             }, this);
17611         }
17612         if (typeof(this.events.edit.listeners) != 'undefined') {
17613             
17614             this.editicon = this.wrap.createChild(
17615                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17616             if (this.addicon) {
17617                 this.editicon.setStyle('margin-left', '40px');
17618             }
17619             this.editicon.on('click', function(e) {
17620                 
17621                 // we fire even  if inothing is selected..
17622                 this.fireEvent('edit', this, this.lastData );
17623                 
17624             }, this);
17625         }
17626         */
17627         
17628         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17629             "up" : function(e){
17630                 this.inKeyMode = true;
17631                 this.selectPrev();
17632             },
17633
17634             "down" : function(e){
17635                 if(!this.isExpanded()){
17636                     this.onTriggerClick();
17637                 }else{
17638                     this.inKeyMode = true;
17639                     this.selectNext();
17640                 }
17641             },
17642
17643             "enter" : function(e){
17644 //                this.onViewClick();
17645                 //return true;
17646                 this.collapse();
17647                 
17648                 if(this.fireEvent("specialkey", this, e)){
17649                     this.onViewClick(false);
17650                 }
17651                 
17652                 return true;
17653             },
17654
17655             "esc" : function(e){
17656                 this.collapse();
17657             },
17658
17659             "tab" : function(e){
17660                 this.collapse();
17661                 
17662                 if(this.fireEvent("specialkey", this, e)){
17663                     this.onViewClick(false);
17664                 }
17665                 
17666                 return true;
17667             },
17668
17669             scope : this,
17670
17671             doRelay : function(foo, bar, hname){
17672                 if(hname == 'down' || this.scope.isExpanded()){
17673                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17674                 }
17675                 return true;
17676             },
17677
17678             forceKeyDown: true
17679         });
17680         
17681         
17682         this.queryDelay = Math.max(this.queryDelay || 10,
17683                 this.mode == 'local' ? 10 : 250);
17684         
17685         
17686         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17687         
17688         if(this.typeAhead){
17689             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17690         }
17691         if(this.editable !== false){
17692             this.inputEl().on("keyup", this.onKeyUp, this);
17693         }
17694         if(this.forceSelection){
17695             this.inputEl().on('blur', this.doForce, this);
17696         }
17697         
17698         if(this.multiple){
17699             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17700             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17701         }
17702     },
17703     
17704     initTickableEvents: function()
17705     {   
17706         this.createList();
17707         
17708         if(this.hiddenName){
17709             
17710             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17711             
17712             this.hiddenField.dom.value =
17713                 this.hiddenValue !== undefined ? this.hiddenValue :
17714                 this.value !== undefined ? this.value : '';
17715
17716             // prevent input submission
17717             this.el.dom.removeAttribute('name');
17718             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17719              
17720              
17721         }
17722         
17723 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17724         
17725         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17726         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17727         if(this.triggerList){
17728             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17729         }
17730          
17731         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17732         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17733         
17734         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17735         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17736         
17737         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17738         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17739         
17740         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17741         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17742         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17743         
17744         this.okBtn.hide();
17745         this.cancelBtn.hide();
17746         
17747         var _this = this;
17748         
17749         (function(){
17750             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17751             _this.list.setWidth(lw);
17752         }).defer(100);
17753         
17754         this.list.on('mouseover', this.onViewOver, this);
17755         this.list.on('mousemove', this.onViewMove, this);
17756         
17757         this.list.on('scroll', this.onViewScroll, this);
17758         
17759         if(!this.tpl){
17760             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17761                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17762         }
17763
17764         this.view = new Roo.View(this.list, this.tpl, {
17765             singleSelect:true,
17766             tickable:true,
17767             parent:this,
17768             store: this.store,
17769             selectedClass: this.selectedClass
17770         });
17771         
17772         //this.view.wrapEl.setDisplayed(false);
17773         this.view.on('click', this.onViewClick, this);
17774         
17775         
17776         
17777         this.store.on('beforeload', this.onBeforeLoad, this);
17778         this.store.on('load', this.onLoad, this);
17779         this.store.on('loadexception', this.onLoadException, this);
17780         
17781         if(this.editable){
17782             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17783                 "up" : function(e){
17784                     this.inKeyMode = true;
17785                     this.selectPrev();
17786                 },
17787
17788                 "down" : function(e){
17789                     this.inKeyMode = true;
17790                     this.selectNext();
17791                 },
17792
17793                 "enter" : function(e){
17794                     if(this.fireEvent("specialkey", this, e)){
17795                         this.onViewClick(false);
17796                     }
17797                     
17798                     return true;
17799                 },
17800
17801                 "esc" : function(e){
17802                     this.onTickableFooterButtonClick(e, false, false);
17803                 },
17804
17805                 "tab" : function(e){
17806                     this.fireEvent("specialkey", this, e);
17807                     
17808                     this.onTickableFooterButtonClick(e, false, false);
17809                     
17810                     return true;
17811                 },
17812
17813                 scope : this,
17814
17815                 doRelay : function(e, fn, key){
17816                     if(this.scope.isExpanded()){
17817                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17818                     }
17819                     return true;
17820                 },
17821
17822                 forceKeyDown: true
17823             });
17824         }
17825         
17826         this.queryDelay = Math.max(this.queryDelay || 10,
17827                 this.mode == 'local' ? 10 : 250);
17828         
17829         
17830         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17831         
17832         if(this.typeAhead){
17833             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17834         }
17835         
17836         if(this.editable !== false){
17837             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17838         }
17839         
17840         this.indicator = this.indicatorEl();
17841         
17842         if(this.indicator){
17843             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17844             this.indicator.hide();
17845         }
17846         
17847     },
17848
17849     onDestroy : function(){
17850         if(this.view){
17851             this.view.setStore(null);
17852             this.view.el.removeAllListeners();
17853             this.view.el.remove();
17854             this.view.purgeListeners();
17855         }
17856         if(this.list){
17857             this.list.dom.innerHTML  = '';
17858         }
17859         
17860         if(this.store){
17861             this.store.un('beforeload', this.onBeforeLoad, this);
17862             this.store.un('load', this.onLoad, this);
17863             this.store.un('loadexception', this.onLoadException, this);
17864         }
17865         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17866     },
17867
17868     // private
17869     fireKey : function(e){
17870         if(e.isNavKeyPress() && !this.list.isVisible()){
17871             this.fireEvent("specialkey", this, e);
17872         }
17873     },
17874
17875     // private
17876     onResize: function(w, h)
17877     {
17878         
17879         
17880 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17881 //        
17882 //        if(typeof w != 'number'){
17883 //            // we do not handle it!?!?
17884 //            return;
17885 //        }
17886 //        var tw = this.trigger.getWidth();
17887 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17888 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17889 //        var x = w - tw;
17890 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17891 //            
17892 //        //this.trigger.setStyle('left', x+'px');
17893 //        
17894 //        if(this.list && this.listWidth === undefined){
17895 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17896 //            this.list.setWidth(lw);
17897 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17898 //        }
17899         
17900     
17901         
17902     },
17903
17904     /**
17905      * Allow or prevent the user from directly editing the field text.  If false is passed,
17906      * the user will only be able to select from the items defined in the dropdown list.  This method
17907      * is the runtime equivalent of setting the 'editable' config option at config time.
17908      * @param {Boolean} value True to allow the user to directly edit the field text
17909      */
17910     setEditable : function(value){
17911         if(value == this.editable){
17912             return;
17913         }
17914         this.editable = value;
17915         if(!value){
17916             this.inputEl().dom.setAttribute('readOnly', true);
17917             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17918             this.inputEl().addClass('x-combo-noedit');
17919         }else{
17920             this.inputEl().dom.removeAttribute('readOnly');
17921             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17922             this.inputEl().removeClass('x-combo-noedit');
17923         }
17924     },
17925
17926     // private
17927     
17928     onBeforeLoad : function(combo,opts){
17929         if(!this.hasFocus){
17930             return;
17931         }
17932          if (!opts.add) {
17933             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17934          }
17935         this.restrictHeight();
17936         this.selectedIndex = -1;
17937     },
17938
17939     // private
17940     onLoad : function(){
17941         
17942         this.hasQuery = false;
17943         
17944         if(!this.hasFocus){
17945             return;
17946         }
17947         
17948         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17949             this.loading.hide();
17950         }
17951         
17952         if(this.store.getCount() > 0){
17953             
17954             this.expand();
17955             this.restrictHeight();
17956             if(this.lastQuery == this.allQuery){
17957                 if(this.editable && !this.tickable){
17958                     this.inputEl().dom.select();
17959                 }
17960                 
17961                 if(
17962                     !this.selectByValue(this.value, true) &&
17963                     this.autoFocus && 
17964                     (
17965                         !this.store.lastOptions ||
17966                         typeof(this.store.lastOptions.add) == 'undefined' || 
17967                         this.store.lastOptions.add != true
17968                     )
17969                 ){
17970                     this.select(0, true);
17971                 }
17972             }else{
17973                 if(this.autoFocus){
17974                     this.selectNext();
17975                 }
17976                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17977                     this.taTask.delay(this.typeAheadDelay);
17978                 }
17979             }
17980         }else{
17981             this.onEmptyResults();
17982         }
17983         
17984         //this.el.focus();
17985     },
17986     // private
17987     onLoadException : function()
17988     {
17989         this.hasQuery = false;
17990         
17991         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17992             this.loading.hide();
17993         }
17994         
17995         if(this.tickable && this.editable){
17996             return;
17997         }
17998         
17999         this.collapse();
18000         // only causes errors at present
18001         //Roo.log(this.store.reader.jsonData);
18002         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18003             // fixme
18004             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18005         //}
18006         
18007         
18008     },
18009     // private
18010     onTypeAhead : function(){
18011         if(this.store.getCount() > 0){
18012             var r = this.store.getAt(0);
18013             var newValue = r.data[this.displayField];
18014             var len = newValue.length;
18015             var selStart = this.getRawValue().length;
18016             
18017             if(selStart != len){
18018                 this.setRawValue(newValue);
18019                 this.selectText(selStart, newValue.length);
18020             }
18021         }
18022     },
18023
18024     // private
18025     onSelect : function(record, index){
18026         
18027         if(this.fireEvent('beforeselect', this, record, index) !== false){
18028         
18029             this.setFromData(index > -1 ? record.data : false);
18030             
18031             this.collapse();
18032             this.fireEvent('select', this, record, index);
18033         }
18034     },
18035
18036     /**
18037      * Returns the currently selected field value or empty string if no value is set.
18038      * @return {String} value The selected value
18039      */
18040     getValue : function()
18041     {
18042         if(Roo.isIOS && this.useNativeIOS){
18043             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18044         }
18045         
18046         if(this.multiple){
18047             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18048         }
18049         
18050         if(this.valueField){
18051             return typeof this.value != 'undefined' ? this.value : '';
18052         }else{
18053             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18054         }
18055     },
18056     
18057     getRawValue : function()
18058     {
18059         if(Roo.isIOS && this.useNativeIOS){
18060             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18061         }
18062         
18063         var v = this.inputEl().getValue();
18064         
18065         return v;
18066     },
18067
18068     /**
18069      * Clears any text/value currently set in the field
18070      */
18071     clearValue : function(){
18072         
18073         if(this.hiddenField){
18074             this.hiddenField.dom.value = '';
18075         }
18076         this.value = '';
18077         this.setRawValue('');
18078         this.lastSelectionText = '';
18079         this.lastData = false;
18080         
18081         var close = this.closeTriggerEl();
18082         
18083         if(close){
18084             close.hide();
18085         }
18086         
18087         this.validate();
18088         
18089     },
18090
18091     /**
18092      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18093      * will be displayed in the field.  If the value does not match the data value of an existing item,
18094      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18095      * Otherwise the field will be blank (although the value will still be set).
18096      * @param {String} value The value to match
18097      */
18098     setValue : function(v)
18099     {
18100         if(Roo.isIOS && this.useNativeIOS){
18101             this.setIOSValue(v);
18102             return;
18103         }
18104         
18105         if(this.multiple){
18106             this.syncValue();
18107             return;
18108         }
18109         
18110         var text = v;
18111         if(this.valueField){
18112             var r = this.findRecord(this.valueField, v);
18113             if(r){
18114                 text = r.data[this.displayField];
18115             }else if(this.valueNotFoundText !== undefined){
18116                 text = this.valueNotFoundText;
18117             }
18118         }
18119         this.lastSelectionText = text;
18120         if(this.hiddenField){
18121             this.hiddenField.dom.value = v;
18122         }
18123         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18124         this.value = v;
18125         
18126         var close = this.closeTriggerEl();
18127         
18128         if(close){
18129             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18130         }
18131         
18132         this.validate();
18133     },
18134     /**
18135      * @property {Object} the last set data for the element
18136      */
18137     
18138     lastData : false,
18139     /**
18140      * Sets the value of the field based on a object which is related to the record format for the store.
18141      * @param {Object} value the value to set as. or false on reset?
18142      */
18143     setFromData : function(o){
18144         
18145         if(this.multiple){
18146             this.addItem(o);
18147             return;
18148         }
18149             
18150         var dv = ''; // display value
18151         var vv = ''; // value value..
18152         this.lastData = o;
18153         if (this.displayField) {
18154             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18155         } else {
18156             // this is an error condition!!!
18157             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18158         }
18159         
18160         if(this.valueField){
18161             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18162         }
18163         
18164         var close = this.closeTriggerEl();
18165         
18166         if(close){
18167             if(dv.length || vv * 1 > 0){
18168                 close.show() ;
18169                 this.blockFocus=true;
18170             } else {
18171                 close.hide();
18172             }             
18173         }
18174         
18175         if(this.hiddenField){
18176             this.hiddenField.dom.value = vv;
18177             
18178             this.lastSelectionText = dv;
18179             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18180             this.value = vv;
18181             return;
18182         }
18183         // no hidden field.. - we store the value in 'value', but still display
18184         // display field!!!!
18185         this.lastSelectionText = dv;
18186         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18187         this.value = vv;
18188         
18189         
18190         
18191     },
18192     // private
18193     reset : function(){
18194         // overridden so that last data is reset..
18195         
18196         if(this.multiple){
18197             this.clearItem();
18198             return;
18199         }
18200         
18201         this.setValue(this.originalValue);
18202         //this.clearInvalid();
18203         this.lastData = false;
18204         if (this.view) {
18205             this.view.clearSelections();
18206         }
18207         
18208         this.validate();
18209     },
18210     // private
18211     findRecord : function(prop, value){
18212         var record;
18213         if(this.store.getCount() > 0){
18214             this.store.each(function(r){
18215                 if(r.data[prop] == value){
18216                     record = r;
18217                     return false;
18218                 }
18219                 return true;
18220             });
18221         }
18222         return record;
18223     },
18224     
18225     getName: function()
18226     {
18227         // returns hidden if it's set..
18228         if (!this.rendered) {return ''};
18229         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18230         
18231     },
18232     // private
18233     onViewMove : function(e, t){
18234         this.inKeyMode = false;
18235     },
18236
18237     // private
18238     onViewOver : function(e, t){
18239         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18240             return;
18241         }
18242         var item = this.view.findItemFromChild(t);
18243         
18244         if(item){
18245             var index = this.view.indexOf(item);
18246             this.select(index, false);
18247         }
18248     },
18249
18250     // private
18251     onViewClick : function(view, doFocus, el, e)
18252     {
18253         var index = this.view.getSelectedIndexes()[0];
18254         
18255         var r = this.store.getAt(index);
18256         
18257         if(this.tickable){
18258             
18259             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18260                 return;
18261             }
18262             
18263             var rm = false;
18264             var _this = this;
18265             
18266             Roo.each(this.tickItems, function(v,k){
18267                 
18268                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18269                     Roo.log(v);
18270                     _this.tickItems.splice(k, 1);
18271                     
18272                     if(typeof(e) == 'undefined' && view == false){
18273                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18274                     }
18275                     
18276                     rm = true;
18277                     return;
18278                 }
18279             });
18280             
18281             if(rm){
18282                 return;
18283             }
18284             
18285             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18286                 this.tickItems.push(r.data);
18287             }
18288             
18289             if(typeof(e) == 'undefined' && view == false){
18290                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18291             }
18292                     
18293             return;
18294         }
18295         
18296         if(r){
18297             this.onSelect(r, index);
18298         }
18299         if(doFocus !== false && !this.blockFocus){
18300             this.inputEl().focus();
18301         }
18302     },
18303
18304     // private
18305     restrictHeight : function(){
18306         //this.innerList.dom.style.height = '';
18307         //var inner = this.innerList.dom;
18308         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18309         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18310         //this.list.beginUpdate();
18311         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18312         this.list.alignTo(this.inputEl(), this.listAlign);
18313         this.list.alignTo(this.inputEl(), this.listAlign);
18314         //this.list.endUpdate();
18315     },
18316
18317     // private
18318     onEmptyResults : function(){
18319         
18320         if(this.tickable && this.editable){
18321             this.hasFocus = false;
18322             this.restrictHeight();
18323             return;
18324         }
18325         
18326         this.collapse();
18327     },
18328
18329     /**
18330      * Returns true if the dropdown list is expanded, else false.
18331      */
18332     isExpanded : function(){
18333         return this.list.isVisible();
18334     },
18335
18336     /**
18337      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18338      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18339      * @param {String} value The data value of the item to select
18340      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18341      * selected item if it is not currently in view (defaults to true)
18342      * @return {Boolean} True if the value matched an item in the list, else false
18343      */
18344     selectByValue : function(v, scrollIntoView){
18345         if(v !== undefined && v !== null){
18346             var r = this.findRecord(this.valueField || this.displayField, v);
18347             if(r){
18348                 this.select(this.store.indexOf(r), scrollIntoView);
18349                 return true;
18350             }
18351         }
18352         return false;
18353     },
18354
18355     /**
18356      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18357      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18358      * @param {Number} index The zero-based index of the list item to select
18359      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18360      * selected item if it is not currently in view (defaults to true)
18361      */
18362     select : function(index, scrollIntoView){
18363         this.selectedIndex = index;
18364         this.view.select(index);
18365         if(scrollIntoView !== false){
18366             var el = this.view.getNode(index);
18367             /*
18368              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18369              */
18370             if(el){
18371                 this.list.scrollChildIntoView(el, false);
18372             }
18373         }
18374     },
18375
18376     // private
18377     selectNext : function(){
18378         var ct = this.store.getCount();
18379         if(ct > 0){
18380             if(this.selectedIndex == -1){
18381                 this.select(0);
18382             }else if(this.selectedIndex < ct-1){
18383                 this.select(this.selectedIndex+1);
18384             }
18385         }
18386     },
18387
18388     // private
18389     selectPrev : function(){
18390         var ct = this.store.getCount();
18391         if(ct > 0){
18392             if(this.selectedIndex == -1){
18393                 this.select(0);
18394             }else if(this.selectedIndex != 0){
18395                 this.select(this.selectedIndex-1);
18396             }
18397         }
18398     },
18399
18400     // private
18401     onKeyUp : function(e){
18402         if(this.editable !== false && !e.isSpecialKey()){
18403             this.lastKey = e.getKey();
18404             this.dqTask.delay(this.queryDelay);
18405         }
18406     },
18407
18408     // private
18409     validateBlur : function(){
18410         return !this.list || !this.list.isVisible();   
18411     },
18412
18413     // private
18414     initQuery : function(){
18415         
18416         var v = this.getRawValue();
18417         
18418         if(this.tickable && this.editable){
18419             v = this.tickableInputEl().getValue();
18420         }
18421         
18422         this.doQuery(v);
18423     },
18424
18425     // private
18426     doForce : function(){
18427         if(this.inputEl().dom.value.length > 0){
18428             this.inputEl().dom.value =
18429                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18430              
18431         }
18432     },
18433
18434     /**
18435      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18436      * query allowing the query action to be canceled if needed.
18437      * @param {String} query The SQL query to execute
18438      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18439      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18440      * saved in the current store (defaults to false)
18441      */
18442     doQuery : function(q, forceAll){
18443         
18444         if(q === undefined || q === null){
18445             q = '';
18446         }
18447         var qe = {
18448             query: q,
18449             forceAll: forceAll,
18450             combo: this,
18451             cancel:false
18452         };
18453         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18454             return false;
18455         }
18456         q = qe.query;
18457         
18458         forceAll = qe.forceAll;
18459         if(forceAll === true || (q.length >= this.minChars)){
18460             
18461             this.hasQuery = true;
18462             
18463             if(this.lastQuery != q || this.alwaysQuery){
18464                 this.lastQuery = q;
18465                 if(this.mode == 'local'){
18466                     this.selectedIndex = -1;
18467                     if(forceAll){
18468                         this.store.clearFilter();
18469                     }else{
18470                         
18471                         if(this.specialFilter){
18472                             this.fireEvent('specialfilter', this);
18473                             this.onLoad();
18474                             return;
18475                         }
18476                         
18477                         this.store.filter(this.displayField, q);
18478                     }
18479                     
18480                     this.store.fireEvent("datachanged", this.store);
18481                     
18482                     this.onLoad();
18483                     
18484                     
18485                 }else{
18486                     
18487                     this.store.baseParams[this.queryParam] = q;
18488                     
18489                     var options = {params : this.getParams(q)};
18490                     
18491                     if(this.loadNext){
18492                         options.add = true;
18493                         options.params.start = this.page * this.pageSize;
18494                     }
18495                     
18496                     this.store.load(options);
18497                     
18498                     /*
18499                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18500                      *  we should expand the list on onLoad
18501                      *  so command out it
18502                      */
18503 //                    this.expand();
18504                 }
18505             }else{
18506                 this.selectedIndex = -1;
18507                 this.onLoad();   
18508             }
18509         }
18510         
18511         this.loadNext = false;
18512     },
18513     
18514     // private
18515     getParams : function(q){
18516         var p = {};
18517         //p[this.queryParam] = q;
18518         
18519         if(this.pageSize){
18520             p.start = 0;
18521             p.limit = this.pageSize;
18522         }
18523         return p;
18524     },
18525
18526     /**
18527      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18528      */
18529     collapse : function(){
18530         if(!this.isExpanded()){
18531             return;
18532         }
18533         
18534         this.list.hide();
18535         
18536         this.hasFocus = false;
18537         
18538         if(this.tickable){
18539             this.okBtn.hide();
18540             this.cancelBtn.hide();
18541             this.trigger.show();
18542             
18543             if(this.editable){
18544                 this.tickableInputEl().dom.value = '';
18545                 this.tickableInputEl().blur();
18546             }
18547             
18548         }
18549         
18550         Roo.get(document).un('mousedown', this.collapseIf, this);
18551         Roo.get(document).un('mousewheel', this.collapseIf, this);
18552         if (!this.editable) {
18553             Roo.get(document).un('keydown', this.listKeyPress, this);
18554         }
18555         this.fireEvent('collapse', this);
18556         
18557         this.validate();
18558     },
18559
18560     // private
18561     collapseIf : function(e){
18562         var in_combo  = e.within(this.el);
18563         var in_list =  e.within(this.list);
18564         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18565         
18566         if (in_combo || in_list || is_list) {
18567             //e.stopPropagation();
18568             return;
18569         }
18570         
18571         if(this.tickable){
18572             this.onTickableFooterButtonClick(e, false, false);
18573         }
18574
18575         this.collapse();
18576         
18577     },
18578
18579     /**
18580      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18581      */
18582     expand : function(){
18583        
18584         if(this.isExpanded() || !this.hasFocus){
18585             return;
18586         }
18587         
18588         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18589         this.list.setWidth(lw);
18590         
18591         Roo.log('expand');
18592         
18593         this.list.show();
18594         
18595         this.restrictHeight();
18596         
18597         if(this.tickable){
18598             
18599             this.tickItems = Roo.apply([], this.item);
18600             
18601             this.okBtn.show();
18602             this.cancelBtn.show();
18603             this.trigger.hide();
18604             
18605             if(this.editable){
18606                 this.tickableInputEl().focus();
18607             }
18608             
18609         }
18610         
18611         Roo.get(document).on('mousedown', this.collapseIf, this);
18612         Roo.get(document).on('mousewheel', this.collapseIf, this);
18613         if (!this.editable) {
18614             Roo.get(document).on('keydown', this.listKeyPress, this);
18615         }
18616         
18617         this.fireEvent('expand', this);
18618     },
18619
18620     // private
18621     // Implements the default empty TriggerField.onTriggerClick function
18622     onTriggerClick : function(e)
18623     {
18624         Roo.log('trigger click');
18625         
18626         if(this.disabled || !this.triggerList){
18627             return;
18628         }
18629         
18630         this.page = 0;
18631         this.loadNext = false;
18632         
18633         if(this.isExpanded()){
18634             this.collapse();
18635             if (!this.blockFocus) {
18636                 this.inputEl().focus();
18637             }
18638             
18639         }else {
18640             this.hasFocus = true;
18641             if(this.triggerAction == 'all') {
18642                 this.doQuery(this.allQuery, true);
18643             } else {
18644                 this.doQuery(this.getRawValue());
18645             }
18646             if (!this.blockFocus) {
18647                 this.inputEl().focus();
18648             }
18649         }
18650     },
18651     
18652     onTickableTriggerClick : function(e)
18653     {
18654         if(this.disabled){
18655             return;
18656         }
18657         
18658         this.page = 0;
18659         this.loadNext = false;
18660         this.hasFocus = true;
18661         
18662         if(this.triggerAction == 'all') {
18663             this.doQuery(this.allQuery, true);
18664         } else {
18665             this.doQuery(this.getRawValue());
18666         }
18667     },
18668     
18669     onSearchFieldClick : function(e)
18670     {
18671         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18672             this.onTickableFooterButtonClick(e, false, false);
18673             return;
18674         }
18675         
18676         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18677             return;
18678         }
18679         
18680         this.page = 0;
18681         this.loadNext = false;
18682         this.hasFocus = true;
18683         
18684         if(this.triggerAction == 'all') {
18685             this.doQuery(this.allQuery, true);
18686         } else {
18687             this.doQuery(this.getRawValue());
18688         }
18689     },
18690     
18691     listKeyPress : function(e)
18692     {
18693         //Roo.log('listkeypress');
18694         // scroll to first matching element based on key pres..
18695         if (e.isSpecialKey()) {
18696             return false;
18697         }
18698         var k = String.fromCharCode(e.getKey()).toUpperCase();
18699         //Roo.log(k);
18700         var match  = false;
18701         var csel = this.view.getSelectedNodes();
18702         var cselitem = false;
18703         if (csel.length) {
18704             var ix = this.view.indexOf(csel[0]);
18705             cselitem  = this.store.getAt(ix);
18706             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18707                 cselitem = false;
18708             }
18709             
18710         }
18711         
18712         this.store.each(function(v) { 
18713             if (cselitem) {
18714                 // start at existing selection.
18715                 if (cselitem.id == v.id) {
18716                     cselitem = false;
18717                 }
18718                 return true;
18719             }
18720                 
18721             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18722                 match = this.store.indexOf(v);
18723                 return false;
18724             }
18725             return true;
18726         }, this);
18727         
18728         if (match === false) {
18729             return true; // no more action?
18730         }
18731         // scroll to?
18732         this.view.select(match);
18733         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18734         sn.scrollIntoView(sn.dom.parentNode, false);
18735     },
18736     
18737     onViewScroll : function(e, t){
18738         
18739         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){
18740             return;
18741         }
18742         
18743         this.hasQuery = true;
18744         
18745         this.loading = this.list.select('.loading', true).first();
18746         
18747         if(this.loading === null){
18748             this.list.createChild({
18749                 tag: 'div',
18750                 cls: 'loading roo-select2-more-results roo-select2-active',
18751                 html: 'Loading more results...'
18752             });
18753             
18754             this.loading = this.list.select('.loading', true).first();
18755             
18756             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18757             
18758             this.loading.hide();
18759         }
18760         
18761         this.loading.show();
18762         
18763         var _combo = this;
18764         
18765         this.page++;
18766         this.loadNext = true;
18767         
18768         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18769         
18770         return;
18771     },
18772     
18773     addItem : function(o)
18774     {   
18775         var dv = ''; // display value
18776         
18777         if (this.displayField) {
18778             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18779         } else {
18780             // this is an error condition!!!
18781             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18782         }
18783         
18784         if(!dv.length){
18785             return;
18786         }
18787         
18788         var choice = this.choices.createChild({
18789             tag: 'li',
18790             cls: 'roo-select2-search-choice',
18791             cn: [
18792                 {
18793                     tag: 'div',
18794                     html: dv
18795                 },
18796                 {
18797                     tag: 'a',
18798                     href: '#',
18799                     cls: 'roo-select2-search-choice-close fa fa-times',
18800                     tabindex: '-1'
18801                 }
18802             ]
18803             
18804         }, this.searchField);
18805         
18806         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18807         
18808         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18809         
18810         this.item.push(o);
18811         
18812         this.lastData = o;
18813         
18814         this.syncValue();
18815         
18816         this.inputEl().dom.value = '';
18817         
18818         this.validate();
18819     },
18820     
18821     onRemoveItem : function(e, _self, o)
18822     {
18823         e.preventDefault();
18824         
18825         this.lastItem = Roo.apply([], this.item);
18826         
18827         var index = this.item.indexOf(o.data) * 1;
18828         
18829         if( index < 0){
18830             Roo.log('not this item?!');
18831             return;
18832         }
18833         
18834         this.item.splice(index, 1);
18835         o.item.remove();
18836         
18837         this.syncValue();
18838         
18839         this.fireEvent('remove', this, e);
18840         
18841         this.validate();
18842         
18843     },
18844     
18845     syncValue : function()
18846     {
18847         if(!this.item.length){
18848             this.clearValue();
18849             return;
18850         }
18851             
18852         var value = [];
18853         var _this = this;
18854         Roo.each(this.item, function(i){
18855             if(_this.valueField){
18856                 value.push(i[_this.valueField]);
18857                 return;
18858             }
18859
18860             value.push(i);
18861         });
18862
18863         this.value = value.join(',');
18864
18865         if(this.hiddenField){
18866             this.hiddenField.dom.value = this.value;
18867         }
18868         
18869         this.store.fireEvent("datachanged", this.store);
18870         
18871         this.validate();
18872     },
18873     
18874     clearItem : function()
18875     {
18876         if(!this.multiple){
18877             return;
18878         }
18879         
18880         this.item = [];
18881         
18882         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18883            c.remove();
18884         });
18885         
18886         this.syncValue();
18887         
18888         this.validate();
18889         
18890         if(this.tickable && !Roo.isTouch){
18891             this.view.refresh();
18892         }
18893     },
18894     
18895     inputEl: function ()
18896     {
18897         if(Roo.isIOS && this.useNativeIOS){
18898             return this.el.select('select.roo-ios-select', true).first();
18899         }
18900         
18901         if(Roo.isTouch && this.mobileTouchView){
18902             return this.el.select('input.form-control',true).first();
18903         }
18904         
18905         if(this.tickable){
18906             return this.searchField;
18907         }
18908         
18909         return this.el.select('input.form-control',true).first();
18910     },
18911     
18912     onTickableFooterButtonClick : function(e, btn, el)
18913     {
18914         e.preventDefault();
18915         
18916         this.lastItem = Roo.apply([], this.item);
18917         
18918         if(btn && btn.name == 'cancel'){
18919             this.tickItems = Roo.apply([], this.item);
18920             this.collapse();
18921             return;
18922         }
18923         
18924         this.clearItem();
18925         
18926         var _this = this;
18927         
18928         Roo.each(this.tickItems, function(o){
18929             _this.addItem(o);
18930         });
18931         
18932         this.collapse();
18933         
18934     },
18935     
18936     validate : function()
18937     {
18938         if(this.getVisibilityEl().hasClass('hidden')){
18939             return true;
18940         }
18941         
18942         var v = this.getRawValue();
18943         
18944         if(this.multiple){
18945             v = this.getValue();
18946         }
18947         
18948         if(this.disabled || this.allowBlank || v.length){
18949             this.markValid();
18950             return true;
18951         }
18952         
18953         this.markInvalid();
18954         return false;
18955     },
18956     
18957     tickableInputEl : function()
18958     {
18959         if(!this.tickable || !this.editable){
18960             return this.inputEl();
18961         }
18962         
18963         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18964     },
18965     
18966     
18967     getAutoCreateTouchView : function()
18968     {
18969         var id = Roo.id();
18970         
18971         var cfg = {
18972             cls: 'form-group' //input-group
18973         };
18974         
18975         var input =  {
18976             tag: 'input',
18977             id : id,
18978             type : this.inputType,
18979             cls : 'form-control x-combo-noedit',
18980             autocomplete: 'new-password',
18981             placeholder : this.placeholder || '',
18982             readonly : true
18983         };
18984         
18985         if (this.name) {
18986             input.name = this.name;
18987         }
18988         
18989         if (this.size) {
18990             input.cls += ' input-' + this.size;
18991         }
18992         
18993         if (this.disabled) {
18994             input.disabled = true;
18995         }
18996         
18997         var inputblock = {
18998             cls : 'roo-combobox-wrap',
18999             cn : [
19000                 input
19001             ]
19002         };
19003         
19004         if(this.before){
19005             inputblock.cls += ' input-group';
19006             
19007             inputblock.cn.unshift({
19008                 tag :'span',
19009                 cls : 'input-group-addon input-group-prepend input-group-text',
19010                 html : this.before
19011             });
19012         }
19013         
19014         if(this.removable && !this.multiple){
19015             inputblock.cls += ' roo-removable';
19016             
19017             inputblock.cn.push({
19018                 tag: 'button',
19019                 html : 'x',
19020                 cls : 'roo-combo-removable-btn close'
19021             });
19022         }
19023
19024         if(this.hasFeedback && !this.allowBlank){
19025             
19026             inputblock.cls += ' has-feedback';
19027             
19028             inputblock.cn.push({
19029                 tag: 'span',
19030                 cls: 'glyphicon form-control-feedback'
19031             });
19032             
19033         }
19034         
19035         if (this.after) {
19036             
19037             inputblock.cls += (this.before) ? '' : ' input-group';
19038             
19039             inputblock.cn.push({
19040                 tag :'span',
19041                 cls : 'input-group-addon input-group-append input-group-text',
19042                 html : this.after
19043             });
19044         }
19045
19046         
19047         var ibwrap = inputblock;
19048         
19049         if(this.multiple){
19050             ibwrap = {
19051                 tag: 'ul',
19052                 cls: 'roo-select2-choices',
19053                 cn:[
19054                     {
19055                         tag: 'li',
19056                         cls: 'roo-select2-search-field',
19057                         cn: [
19058
19059                             inputblock
19060                         ]
19061                     }
19062                 ]
19063             };
19064         
19065             
19066         }
19067         
19068         var combobox = {
19069             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19070             cn: [
19071                 {
19072                     tag: 'input',
19073                     type : 'hidden',
19074                     cls: 'form-hidden-field'
19075                 },
19076                 ibwrap
19077             ]
19078         };
19079         
19080         if(!this.multiple && this.showToggleBtn){
19081             
19082             var caret = {
19083                 cls: 'caret'
19084             };
19085             
19086             if (this.caret != false) {
19087                 caret = {
19088                      tag: 'i',
19089                      cls: 'fa fa-' + this.caret
19090                 };
19091                 
19092             }
19093             
19094             combobox.cn.push({
19095                 tag :'span',
19096                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19097                 cn : [
19098                     Roo.bootstrap.version == 3 ? caret : '',
19099                     {
19100                         tag: 'span',
19101                         cls: 'combobox-clear',
19102                         cn  : [
19103                             {
19104                                 tag : 'i',
19105                                 cls: 'icon-remove'
19106                             }
19107                         ]
19108                     }
19109                 ]
19110
19111             })
19112         }
19113         
19114         if(this.multiple){
19115             combobox.cls += ' roo-select2-container-multi';
19116         }
19117         
19118         var required =  this.allowBlank ?  {
19119                     tag : 'i',
19120                     style: 'display: none'
19121                 } : {
19122                    tag : 'i',
19123                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19124                    tooltip : 'This field is required'
19125                 };
19126         
19127         var align = this.labelAlign || this.parentLabelAlign();
19128         
19129         if (align ==='left' && this.fieldLabel.length) {
19130
19131             cfg.cn = [
19132                 required,
19133                 {
19134                     tag: 'label',
19135                     cls : 'control-label col-form-label',
19136                     html : this.fieldLabel
19137
19138                 },
19139                 {
19140                     cls : 'roo-combobox-wrap ', 
19141                     cn: [
19142                         combobox
19143                     ]
19144                 }
19145             ];
19146             
19147             var labelCfg = cfg.cn[1];
19148             var contentCfg = cfg.cn[2];
19149             
19150
19151             if(this.indicatorpos == 'right'){
19152                 cfg.cn = [
19153                     {
19154                         tag: 'label',
19155                         'for' :  id,
19156                         cls : 'control-label col-form-label',
19157                         cn : [
19158                             {
19159                                 tag : 'span',
19160                                 html : this.fieldLabel
19161                             },
19162                             required
19163                         ]
19164                     },
19165                     {
19166                         cls : "roo-combobox-wrap ",
19167                         cn: [
19168                             combobox
19169                         ]
19170                     }
19171
19172                 ];
19173                 
19174                 labelCfg = cfg.cn[0];
19175                 contentCfg = cfg.cn[1];
19176             }
19177             
19178            
19179             
19180             if(this.labelWidth > 12){
19181                 labelCfg.style = "width: " + this.labelWidth + 'px';
19182             }
19183            
19184             if(this.labelWidth < 13 && this.labelmd == 0){
19185                 this.labelmd = this.labelWidth;
19186             }
19187             
19188             if(this.labellg > 0){
19189                 labelCfg.cls += ' col-lg-' + this.labellg;
19190                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19191             }
19192             
19193             if(this.labelmd > 0){
19194                 labelCfg.cls += ' col-md-' + this.labelmd;
19195                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19196             }
19197             
19198             if(this.labelsm > 0){
19199                 labelCfg.cls += ' col-sm-' + this.labelsm;
19200                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19201             }
19202             
19203             if(this.labelxs > 0){
19204                 labelCfg.cls += ' col-xs-' + this.labelxs;
19205                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19206             }
19207                 
19208                 
19209         } else if ( this.fieldLabel.length) {
19210             cfg.cn = [
19211                required,
19212                 {
19213                     tag: 'label',
19214                     cls : 'control-label',
19215                     html : this.fieldLabel
19216
19217                 },
19218                 {
19219                     cls : '', 
19220                     cn: [
19221                         combobox
19222                     ]
19223                 }
19224             ];
19225             
19226             if(this.indicatorpos == 'right'){
19227                 cfg.cn = [
19228                     {
19229                         tag: 'label',
19230                         cls : 'control-label',
19231                         html : this.fieldLabel,
19232                         cn : [
19233                             required
19234                         ]
19235                     },
19236                     {
19237                         cls : '', 
19238                         cn: [
19239                             combobox
19240                         ]
19241                     }
19242                 ];
19243             }
19244         } else {
19245             cfg.cn = combobox;    
19246         }
19247         
19248         
19249         var settings = this;
19250         
19251         ['xs','sm','md','lg'].map(function(size){
19252             if (settings[size]) {
19253                 cfg.cls += ' col-' + size + '-' + settings[size];
19254             }
19255         });
19256         
19257         return cfg;
19258     },
19259     
19260     initTouchView : function()
19261     {
19262         this.renderTouchView();
19263         
19264         this.touchViewEl.on('scroll', function(){
19265             this.el.dom.scrollTop = 0;
19266         }, this);
19267         
19268         this.originalValue = this.getValue();
19269         
19270         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19271         
19272         this.inputEl().on("click", this.showTouchView, this);
19273         if (this.triggerEl) {
19274             this.triggerEl.on("click", this.showTouchView, this);
19275         }
19276         
19277         
19278         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19279         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19280         
19281         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19282         
19283         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19284         this.store.on('load', this.onTouchViewLoad, this);
19285         this.store.on('loadexception', this.onTouchViewLoadException, this);
19286         
19287         if(this.hiddenName){
19288             
19289             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19290             
19291             this.hiddenField.dom.value =
19292                 this.hiddenValue !== undefined ? this.hiddenValue :
19293                 this.value !== undefined ? this.value : '';
19294         
19295             this.el.dom.removeAttribute('name');
19296             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19297         }
19298         
19299         if(this.multiple){
19300             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19301             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19302         }
19303         
19304         if(this.removable && !this.multiple){
19305             var close = this.closeTriggerEl();
19306             if(close){
19307                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19308                 close.on('click', this.removeBtnClick, this, close);
19309             }
19310         }
19311         /*
19312          * fix the bug in Safari iOS8
19313          */
19314         this.inputEl().on("focus", function(e){
19315             document.activeElement.blur();
19316         }, this);
19317         
19318         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19319         
19320         return;
19321         
19322         
19323     },
19324     
19325     renderTouchView : function()
19326     {
19327         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19328         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19329         
19330         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19331         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19332         
19333         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19334         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19335         this.touchViewBodyEl.setStyle('overflow', 'auto');
19336         
19337         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19338         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19339         
19340         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19341         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19342         
19343     },
19344     
19345     showTouchView : function()
19346     {
19347         if(this.disabled){
19348             return;
19349         }
19350         
19351         this.touchViewHeaderEl.hide();
19352
19353         if(this.modalTitle.length){
19354             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19355             this.touchViewHeaderEl.show();
19356         }
19357
19358         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19359         this.touchViewEl.show();
19360
19361         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19362         
19363         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19364         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19365
19366         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19367
19368         if(this.modalTitle.length){
19369             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19370         }
19371         
19372         this.touchViewBodyEl.setHeight(bodyHeight);
19373
19374         if(this.animate){
19375             var _this = this;
19376             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19377         }else{
19378             this.touchViewEl.addClass(['in','show']);
19379         }
19380         
19381         if(this._touchViewMask){
19382             Roo.get(document.body).addClass("x-body-masked");
19383             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19384             this._touchViewMask.setStyle('z-index', 10000);
19385             this._touchViewMask.addClass('show');
19386         }
19387         
19388         this.doTouchViewQuery();
19389         
19390     },
19391     
19392     hideTouchView : function()
19393     {
19394         this.touchViewEl.removeClass(['in','show']);
19395
19396         if(this.animate){
19397             var _this = this;
19398             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19399         }else{
19400             this.touchViewEl.setStyle('display', 'none');
19401         }
19402         
19403         if(this._touchViewMask){
19404             this._touchViewMask.removeClass('show');
19405             Roo.get(document.body).removeClass("x-body-masked");
19406         }
19407     },
19408     
19409     setTouchViewValue : function()
19410     {
19411         if(this.multiple){
19412             this.clearItem();
19413         
19414             var _this = this;
19415
19416             Roo.each(this.tickItems, function(o){
19417                 this.addItem(o);
19418             }, this);
19419         }
19420         
19421         this.hideTouchView();
19422     },
19423     
19424     doTouchViewQuery : function()
19425     {
19426         var qe = {
19427             query: '',
19428             forceAll: true,
19429             combo: this,
19430             cancel:false
19431         };
19432         
19433         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19434             return false;
19435         }
19436         
19437         if(!this.alwaysQuery || this.mode == 'local'){
19438             this.onTouchViewLoad();
19439             return;
19440         }
19441         
19442         this.store.load();
19443     },
19444     
19445     onTouchViewBeforeLoad : function(combo,opts)
19446     {
19447         return;
19448     },
19449
19450     // private
19451     onTouchViewLoad : function()
19452     {
19453         if(this.store.getCount() < 1){
19454             this.onTouchViewEmptyResults();
19455             return;
19456         }
19457         
19458         this.clearTouchView();
19459         
19460         var rawValue = this.getRawValue();
19461         
19462         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19463         
19464         this.tickItems = [];
19465         
19466         this.store.data.each(function(d, rowIndex){
19467             var row = this.touchViewListGroup.createChild(template);
19468             
19469             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19470                 row.addClass(d.data.cls);
19471             }
19472             
19473             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19474                 var cfg = {
19475                     data : d.data,
19476                     html : d.data[this.displayField]
19477                 };
19478                 
19479                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19480                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19481                 }
19482             }
19483             row.removeClass('selected');
19484             if(!this.multiple && this.valueField &&
19485                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19486             {
19487                 // radio buttons..
19488                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19489                 row.addClass('selected');
19490             }
19491             
19492             if(this.multiple && this.valueField &&
19493                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19494             {
19495                 
19496                 // checkboxes...
19497                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19498                 this.tickItems.push(d.data);
19499             }
19500             
19501             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19502             
19503         }, this);
19504         
19505         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19506         
19507         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19508
19509         if(this.modalTitle.length){
19510             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19511         }
19512
19513         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19514         
19515         if(this.mobile_restrict_height && listHeight < bodyHeight){
19516             this.touchViewBodyEl.setHeight(listHeight);
19517         }
19518         
19519         var _this = this;
19520         
19521         if(firstChecked && listHeight > bodyHeight){
19522             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19523         }
19524         
19525     },
19526     
19527     onTouchViewLoadException : function()
19528     {
19529         this.hideTouchView();
19530     },
19531     
19532     onTouchViewEmptyResults : function()
19533     {
19534         this.clearTouchView();
19535         
19536         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19537         
19538         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19539         
19540     },
19541     
19542     clearTouchView : function()
19543     {
19544         this.touchViewListGroup.dom.innerHTML = '';
19545     },
19546     
19547     onTouchViewClick : function(e, el, o)
19548     {
19549         e.preventDefault();
19550         
19551         var row = o.row;
19552         var rowIndex = o.rowIndex;
19553         
19554         var r = this.store.getAt(rowIndex);
19555         
19556         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19557             
19558             if(!this.multiple){
19559                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19560                     c.dom.removeAttribute('checked');
19561                 }, this);
19562
19563                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19564
19565                 this.setFromData(r.data);
19566
19567                 var close = this.closeTriggerEl();
19568
19569                 if(close){
19570                     close.show();
19571                 }
19572
19573                 this.hideTouchView();
19574
19575                 this.fireEvent('select', this, r, rowIndex);
19576
19577                 return;
19578             }
19579
19580             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19581                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19582                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19583                 return;
19584             }
19585
19586             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19587             this.addItem(r.data);
19588             this.tickItems.push(r.data);
19589         }
19590     },
19591     
19592     getAutoCreateNativeIOS : function()
19593     {
19594         var cfg = {
19595             cls: 'form-group' //input-group,
19596         };
19597         
19598         var combobox =  {
19599             tag: 'select',
19600             cls : 'roo-ios-select'
19601         };
19602         
19603         if (this.name) {
19604             combobox.name = this.name;
19605         }
19606         
19607         if (this.disabled) {
19608             combobox.disabled = true;
19609         }
19610         
19611         var settings = this;
19612         
19613         ['xs','sm','md','lg'].map(function(size){
19614             if (settings[size]) {
19615                 cfg.cls += ' col-' + size + '-' + settings[size];
19616             }
19617         });
19618         
19619         cfg.cn = combobox;
19620         
19621         return cfg;
19622         
19623     },
19624     
19625     initIOSView : function()
19626     {
19627         this.store.on('load', this.onIOSViewLoad, this);
19628         
19629         return;
19630     },
19631     
19632     onIOSViewLoad : function()
19633     {
19634         if(this.store.getCount() < 1){
19635             return;
19636         }
19637         
19638         this.clearIOSView();
19639         
19640         if(this.allowBlank) {
19641             
19642             var default_text = '-- SELECT --';
19643             
19644             if(this.placeholder.length){
19645                 default_text = this.placeholder;
19646             }
19647             
19648             if(this.emptyTitle.length){
19649                 default_text += ' - ' + this.emptyTitle + ' -';
19650             }
19651             
19652             var opt = this.inputEl().createChild({
19653                 tag: 'option',
19654                 value : 0,
19655                 html : default_text
19656             });
19657             
19658             var o = {};
19659             o[this.valueField] = 0;
19660             o[this.displayField] = default_text;
19661             
19662             this.ios_options.push({
19663                 data : o,
19664                 el : opt
19665             });
19666             
19667         }
19668         
19669         this.store.data.each(function(d, rowIndex){
19670             
19671             var html = '';
19672             
19673             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19674                 html = d.data[this.displayField];
19675             }
19676             
19677             var value = '';
19678             
19679             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19680                 value = d.data[this.valueField];
19681             }
19682             
19683             var option = {
19684                 tag: 'option',
19685                 value : value,
19686                 html : html
19687             };
19688             
19689             if(this.value == d.data[this.valueField]){
19690                 option['selected'] = true;
19691             }
19692             
19693             var opt = this.inputEl().createChild(option);
19694             
19695             this.ios_options.push({
19696                 data : d.data,
19697                 el : opt
19698             });
19699             
19700         }, this);
19701         
19702         this.inputEl().on('change', function(){
19703            this.fireEvent('select', this);
19704         }, this);
19705         
19706     },
19707     
19708     clearIOSView: function()
19709     {
19710         this.inputEl().dom.innerHTML = '';
19711         
19712         this.ios_options = [];
19713     },
19714     
19715     setIOSValue: function(v)
19716     {
19717         this.value = v;
19718         
19719         if(!this.ios_options){
19720             return;
19721         }
19722         
19723         Roo.each(this.ios_options, function(opts){
19724            
19725            opts.el.dom.removeAttribute('selected');
19726            
19727            if(opts.data[this.valueField] != v){
19728                return;
19729            }
19730            
19731            opts.el.dom.setAttribute('selected', true);
19732            
19733         }, this);
19734     }
19735
19736     /** 
19737     * @cfg {Boolean} grow 
19738     * @hide 
19739     */
19740     /** 
19741     * @cfg {Number} growMin 
19742     * @hide 
19743     */
19744     /** 
19745     * @cfg {Number} growMax 
19746     * @hide 
19747     */
19748     /**
19749      * @hide
19750      * @method autoSize
19751      */
19752 });
19753
19754 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19755     
19756     header : {
19757         tag: 'div',
19758         cls: 'modal-header',
19759         cn: [
19760             {
19761                 tag: 'h4',
19762                 cls: 'modal-title'
19763             }
19764         ]
19765     },
19766     
19767     body : {
19768         tag: 'div',
19769         cls: 'modal-body',
19770         cn: [
19771             {
19772                 tag: 'ul',
19773                 cls: 'list-group'
19774             }
19775         ]
19776     },
19777     
19778     listItemRadio : {
19779         tag: 'li',
19780         cls: 'list-group-item',
19781         cn: [
19782             {
19783                 tag: 'span',
19784                 cls: 'roo-combobox-list-group-item-value'
19785             },
19786             {
19787                 tag: 'div',
19788                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19789                 cn: [
19790                     {
19791                         tag: 'input',
19792                         type: 'radio'
19793                     },
19794                     {
19795                         tag: 'label'
19796                     }
19797                 ]
19798             }
19799         ]
19800     },
19801     
19802     listItemCheckbox : {
19803         tag: 'li',
19804         cls: 'list-group-item',
19805         cn: [
19806             {
19807                 tag: 'span',
19808                 cls: 'roo-combobox-list-group-item-value'
19809             },
19810             {
19811                 tag: 'div',
19812                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19813                 cn: [
19814                     {
19815                         tag: 'input',
19816                         type: 'checkbox'
19817                     },
19818                     {
19819                         tag: 'label'
19820                     }
19821                 ]
19822             }
19823         ]
19824     },
19825     
19826     emptyResult : {
19827         tag: 'div',
19828         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19829     },
19830     
19831     footer : {
19832         tag: 'div',
19833         cls: 'modal-footer',
19834         cn: [
19835             {
19836                 tag: 'div',
19837                 cls: 'row',
19838                 cn: [
19839                     {
19840                         tag: 'div',
19841                         cls: 'col-xs-6 text-left',
19842                         cn: {
19843                             tag: 'button',
19844                             cls: 'btn btn-danger roo-touch-view-cancel',
19845                             html: 'Cancel'
19846                         }
19847                     },
19848                     {
19849                         tag: 'div',
19850                         cls: 'col-xs-6 text-right',
19851                         cn: {
19852                             tag: 'button',
19853                             cls: 'btn btn-success roo-touch-view-ok',
19854                             html: 'OK'
19855                         }
19856                     }
19857                 ]
19858             }
19859         ]
19860         
19861     }
19862 });
19863
19864 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19865     
19866     touchViewTemplate : {
19867         tag: 'div',
19868         cls: 'modal fade roo-combobox-touch-view',
19869         cn: [
19870             {
19871                 tag: 'div',
19872                 cls: 'modal-dialog',
19873                 style : 'position:fixed', // we have to fix position....
19874                 cn: [
19875                     {
19876                         tag: 'div',
19877                         cls: 'modal-content',
19878                         cn: [
19879                             Roo.bootstrap.form.ComboBox.header,
19880                             Roo.bootstrap.form.ComboBox.body,
19881                             Roo.bootstrap.form.ComboBox.footer
19882                         ]
19883                     }
19884                 ]
19885             }
19886         ]
19887     }
19888 });/*
19889  * Based on:
19890  * Ext JS Library 1.1.1
19891  * Copyright(c) 2006-2007, Ext JS, LLC.
19892  *
19893  * Originally Released Under LGPL - original licence link has changed is not relivant.
19894  *
19895  * Fork - LGPL
19896  * <script type="text/javascript">
19897  */
19898
19899 /**
19900  * @class Roo.View
19901  * @extends Roo.util.Observable
19902  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19903  * This class also supports single and multi selection modes. <br>
19904  * Create a data model bound view:
19905  <pre><code>
19906  var store = new Roo.data.Store(...);
19907
19908  var view = new Roo.View({
19909     el : "my-element",
19910     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19911  
19912     singleSelect: true,
19913     selectedClass: "ydataview-selected",
19914     store: store
19915  });
19916
19917  // listen for node click?
19918  view.on("click", function(vw, index, node, e){
19919  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19920  });
19921
19922  // load XML data
19923  dataModel.load("foobar.xml");
19924  </code></pre>
19925  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19926  * <br><br>
19927  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19928  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19929  * 
19930  * Note: old style constructor is still suported (container, template, config)
19931  * 
19932  * @constructor
19933  * Create a new View
19934  * @param {Object} config The config object
19935  * 
19936  */
19937 Roo.View = function(config, depreciated_tpl, depreciated_config){
19938     
19939     this.parent = false;
19940     
19941     if (typeof(depreciated_tpl) == 'undefined') {
19942         // new way.. - universal constructor.
19943         Roo.apply(this, config);
19944         this.el  = Roo.get(this.el);
19945     } else {
19946         // old format..
19947         this.el  = Roo.get(config);
19948         this.tpl = depreciated_tpl;
19949         Roo.apply(this, depreciated_config);
19950     }
19951     this.wrapEl  = this.el.wrap().wrap();
19952     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19953     
19954     
19955     if(typeof(this.tpl) == "string"){
19956         this.tpl = new Roo.Template(this.tpl);
19957     } else {
19958         // support xtype ctors..
19959         this.tpl = new Roo.factory(this.tpl, Roo);
19960     }
19961     
19962     
19963     this.tpl.compile();
19964     
19965     /** @private */
19966     this.addEvents({
19967         /**
19968          * @event beforeclick
19969          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
19976         /**
19977          * @event click
19978          * Fires when a template node is 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             "click" : true,
19985         /**
19986          * @event dblclick
19987          * Fires when a template node is double clicked.
19988          * @param {Roo.View} this
19989          * @param {Number} index The index of the target node
19990          * @param {HTMLElement} node The target node
19991          * @param {Roo.EventObject} e The raw event object
19992          */
19993             "dblclick" : true,
19994         /**
19995          * @event contextmenu
19996          * Fires when a template node is right clicked.
19997          * @param {Roo.View} this
19998          * @param {Number} index The index of the target node
19999          * @param {HTMLElement} node The target node
20000          * @param {Roo.EventObject} e The raw event object
20001          */
20002             "contextmenu" : true,
20003         /**
20004          * @event selectionchange
20005          * Fires when the selected nodes change.
20006          * @param {Roo.View} this
20007          * @param {Array} selections Array of the selected nodes
20008          */
20009             "selectionchange" : true,
20010     
20011         /**
20012          * @event beforeselect
20013          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20014          * @param {Roo.View} this
20015          * @param {HTMLElement} node The node to be selected
20016          * @param {Array} selections Array of currently selected nodes
20017          */
20018             "beforeselect" : true,
20019         /**
20020          * @event preparedata
20021          * Fires on every row to render, to allow you to change the data.
20022          * @param {Roo.View} this
20023          * @param {Object} data to be rendered (change this)
20024          */
20025           "preparedata" : true
20026           
20027           
20028         });
20029
20030
20031
20032     this.el.on({
20033         "click": this.onClick,
20034         "dblclick": this.onDblClick,
20035         "contextmenu": this.onContextMenu,
20036         scope:this
20037     });
20038
20039     this.selections = [];
20040     this.nodes = [];
20041     this.cmp = new Roo.CompositeElementLite([]);
20042     if(this.store){
20043         this.store = Roo.factory(this.store, Roo.data);
20044         this.setStore(this.store, true);
20045     }
20046     
20047     if ( this.footer && this.footer.xtype) {
20048            
20049          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20050         
20051         this.footer.dataSource = this.store;
20052         this.footer.container = fctr;
20053         this.footer = Roo.factory(this.footer, Roo);
20054         fctr.insertFirst(this.el);
20055         
20056         // this is a bit insane - as the paging toolbar seems to detach the el..
20057 //        dom.parentNode.parentNode.parentNode
20058          // they get detached?
20059     }
20060     
20061     
20062     Roo.View.superclass.constructor.call(this);
20063     
20064     
20065 };
20066
20067 Roo.extend(Roo.View, Roo.util.Observable, {
20068     
20069      /**
20070      * @cfg {Roo.data.Store} store Data store to load data from.
20071      */
20072     store : false,
20073     
20074     /**
20075      * @cfg {String|Roo.Element} el The container element.
20076      */
20077     el : '',
20078     
20079     /**
20080      * @cfg {String|Roo.Template} tpl The template used by this View 
20081      */
20082     tpl : false,
20083     /**
20084      * @cfg {String} dataName the named area of the template to use as the data area
20085      *                          Works with domtemplates roo-name="name"
20086      */
20087     dataName: false,
20088     /**
20089      * @cfg {String} selectedClass The css class to add to selected nodes
20090      */
20091     selectedClass : "x-view-selected",
20092      /**
20093      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20094      */
20095     emptyText : "",
20096     
20097     /**
20098      * @cfg {String} text to display on mask (default Loading)
20099      */
20100     mask : false,
20101     /**
20102      * @cfg {Boolean} multiSelect Allow multiple selection
20103      */
20104     multiSelect : false,
20105     /**
20106      * @cfg {Boolean} singleSelect Allow single selection
20107      */
20108     singleSelect:  false,
20109     
20110     /**
20111      * @cfg {Boolean} toggleSelect - selecting 
20112      */
20113     toggleSelect : false,
20114     
20115     /**
20116      * @cfg {Boolean} tickable - selecting 
20117      */
20118     tickable : false,
20119     
20120     /**
20121      * Returns the element this view is bound to.
20122      * @return {Roo.Element}
20123      */
20124     getEl : function(){
20125         return this.wrapEl;
20126     },
20127     
20128     
20129
20130     /**
20131      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20132      */
20133     refresh : function(){
20134         //Roo.log('refresh');
20135         var t = this.tpl;
20136         
20137         // if we are using something like 'domtemplate', then
20138         // the what gets used is:
20139         // t.applySubtemplate(NAME, data, wrapping data..)
20140         // the outer template then get' applied with
20141         //     the store 'extra data'
20142         // and the body get's added to the
20143         //      roo-name="data" node?
20144         //      <span class='roo-tpl-{name}'></span> ?????
20145         
20146         
20147         
20148         this.clearSelections();
20149         this.el.update("");
20150         var html = [];
20151         var records = this.store.getRange();
20152         if(records.length < 1) {
20153             
20154             // is this valid??  = should it render a template??
20155             
20156             this.el.update(this.emptyText);
20157             return;
20158         }
20159         var el = this.el;
20160         if (this.dataName) {
20161             this.el.update(t.apply(this.store.meta)); //????
20162             el = this.el.child('.roo-tpl-' + this.dataName);
20163         }
20164         
20165         for(var i = 0, len = records.length; i < len; i++){
20166             var data = this.prepareData(records[i].data, i, records[i]);
20167             this.fireEvent("preparedata", this, data, i, records[i]);
20168             
20169             var d = Roo.apply({}, data);
20170             
20171             if(this.tickable){
20172                 Roo.apply(d, {'roo-id' : Roo.id()});
20173                 
20174                 var _this = this;
20175             
20176                 Roo.each(this.parent.item, function(item){
20177                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20178                         return;
20179                     }
20180                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20181                 });
20182             }
20183             
20184             html[html.length] = Roo.util.Format.trim(
20185                 this.dataName ?
20186                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20187                     t.apply(d)
20188             );
20189         }
20190         
20191         
20192         
20193         el.update(html.join(""));
20194         this.nodes = el.dom.childNodes;
20195         this.updateIndexes(0);
20196     },
20197     
20198
20199     /**
20200      * Function to override to reformat the data that is sent to
20201      * the template for each node.
20202      * DEPRICATED - use the preparedata event handler.
20203      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20204      * a JSON object for an UpdateManager bound view).
20205      */
20206     prepareData : function(data, index, record)
20207     {
20208         this.fireEvent("preparedata", this, data, index, record);
20209         return data;
20210     },
20211
20212     onUpdate : function(ds, record){
20213         // Roo.log('on update');   
20214         this.clearSelections();
20215         var index = this.store.indexOf(record);
20216         var n = this.nodes[index];
20217         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20218         n.parentNode.removeChild(n);
20219         this.updateIndexes(index, index);
20220     },
20221
20222     
20223     
20224 // --------- FIXME     
20225     onAdd : function(ds, records, index)
20226     {
20227         //Roo.log(['on Add', ds, records, index] );        
20228         this.clearSelections();
20229         if(this.nodes.length == 0){
20230             this.refresh();
20231             return;
20232         }
20233         var n = this.nodes[index];
20234         for(var i = 0, len = records.length; i < len; i++){
20235             var d = this.prepareData(records[i].data, i, records[i]);
20236             if(n){
20237                 this.tpl.insertBefore(n, d);
20238             }else{
20239                 
20240                 this.tpl.append(this.el, d);
20241             }
20242         }
20243         this.updateIndexes(index);
20244     },
20245
20246     onRemove : function(ds, record, index){
20247        // Roo.log('onRemove');
20248         this.clearSelections();
20249         var el = this.dataName  ?
20250             this.el.child('.roo-tpl-' + this.dataName) :
20251             this.el; 
20252         
20253         el.dom.removeChild(this.nodes[index]);
20254         this.updateIndexes(index);
20255     },
20256
20257     /**
20258      * Refresh an individual node.
20259      * @param {Number} index
20260      */
20261     refreshNode : function(index){
20262         this.onUpdate(this.store, this.store.getAt(index));
20263     },
20264
20265     updateIndexes : function(startIndex, endIndex){
20266         var ns = this.nodes;
20267         startIndex = startIndex || 0;
20268         endIndex = endIndex || ns.length - 1;
20269         for(var i = startIndex; i <= endIndex; i++){
20270             ns[i].nodeIndex = i;
20271         }
20272     },
20273
20274     /**
20275      * Changes the data store this view uses and refresh the view.
20276      * @param {Store} store
20277      */
20278     setStore : function(store, initial){
20279         if(!initial && this.store){
20280             this.store.un("datachanged", this.refresh);
20281             this.store.un("add", this.onAdd);
20282             this.store.un("remove", this.onRemove);
20283             this.store.un("update", this.onUpdate);
20284             this.store.un("clear", this.refresh);
20285             this.store.un("beforeload", this.onBeforeLoad);
20286             this.store.un("load", this.onLoad);
20287             this.store.un("loadexception", this.onLoad);
20288         }
20289         if(store){
20290           
20291             store.on("datachanged", this.refresh, this);
20292             store.on("add", this.onAdd, this);
20293             store.on("remove", this.onRemove, this);
20294             store.on("update", this.onUpdate, this);
20295             store.on("clear", this.refresh, this);
20296             store.on("beforeload", this.onBeforeLoad, this);
20297             store.on("load", this.onLoad, this);
20298             store.on("loadexception", this.onLoad, this);
20299         }
20300         
20301         if(store){
20302             this.refresh();
20303         }
20304     },
20305     /**
20306      * onbeforeLoad - masks the loading area.
20307      *
20308      */
20309     onBeforeLoad : function(store,opts)
20310     {
20311          //Roo.log('onBeforeLoad');   
20312         if (!opts.add) {
20313             this.el.update("");
20314         }
20315         this.el.mask(this.mask ? this.mask : "Loading" ); 
20316     },
20317     onLoad : function ()
20318     {
20319         this.el.unmask();
20320     },
20321     
20322
20323     /**
20324      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20325      * @param {HTMLElement} node
20326      * @return {HTMLElement} The template node
20327      */
20328     findItemFromChild : function(node){
20329         var el = this.dataName  ?
20330             this.el.child('.roo-tpl-' + this.dataName,true) :
20331             this.el.dom; 
20332         
20333         if(!node || node.parentNode == el){
20334                     return node;
20335             }
20336             var p = node.parentNode;
20337             while(p && p != el){
20338             if(p.parentNode == el){
20339                 return p;
20340             }
20341             p = p.parentNode;
20342         }
20343             return null;
20344     },
20345
20346     /** @ignore */
20347     onClick : function(e){
20348         var item = this.findItemFromChild(e.getTarget());
20349         if(item){
20350             var index = this.indexOf(item);
20351             if(this.onItemClick(item, index, e) !== false){
20352                 this.fireEvent("click", this, index, item, e);
20353             }
20354         }else{
20355             this.clearSelections();
20356         }
20357     },
20358
20359     /** @ignore */
20360     onContextMenu : function(e){
20361         var item = this.findItemFromChild(e.getTarget());
20362         if(item){
20363             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20364         }
20365     },
20366
20367     /** @ignore */
20368     onDblClick : function(e){
20369         var item = this.findItemFromChild(e.getTarget());
20370         if(item){
20371             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20372         }
20373     },
20374
20375     onItemClick : function(item, index, e)
20376     {
20377         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20378             return false;
20379         }
20380         if (this.toggleSelect) {
20381             var m = this.isSelected(item) ? 'unselect' : 'select';
20382             //Roo.log(m);
20383             var _t = this;
20384             _t[m](item, true, false);
20385             return true;
20386         }
20387         if(this.multiSelect || this.singleSelect){
20388             if(this.multiSelect && e.shiftKey && this.lastSelection){
20389                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20390             }else{
20391                 this.select(item, this.multiSelect && e.ctrlKey);
20392                 this.lastSelection = item;
20393             }
20394             
20395             if(!this.tickable){
20396                 e.preventDefault();
20397             }
20398             
20399         }
20400         return true;
20401     },
20402
20403     /**
20404      * Get the number of selected nodes.
20405      * @return {Number}
20406      */
20407     getSelectionCount : function(){
20408         return this.selections.length;
20409     },
20410
20411     /**
20412      * Get the currently selected nodes.
20413      * @return {Array} An array of HTMLElements
20414      */
20415     getSelectedNodes : function(){
20416         return this.selections;
20417     },
20418
20419     /**
20420      * Get the indexes of the selected nodes.
20421      * @return {Array}
20422      */
20423     getSelectedIndexes : function(){
20424         var indexes = [], s = this.selections;
20425         for(var i = 0, len = s.length; i < len; i++){
20426             indexes.push(s[i].nodeIndex);
20427         }
20428         return indexes;
20429     },
20430
20431     /**
20432      * Clear all selections
20433      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20434      */
20435     clearSelections : function(suppressEvent){
20436         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20437             this.cmp.elements = this.selections;
20438             this.cmp.removeClass(this.selectedClass);
20439             this.selections = [];
20440             if(!suppressEvent){
20441                 this.fireEvent("selectionchange", this, this.selections);
20442             }
20443         }
20444     },
20445
20446     /**
20447      * Returns true if the passed node is selected
20448      * @param {HTMLElement/Number} node The node or node index
20449      * @return {Boolean}
20450      */
20451     isSelected : function(node){
20452         var s = this.selections;
20453         if(s.length < 1){
20454             return false;
20455         }
20456         node = this.getNode(node);
20457         return s.indexOf(node) !== -1;
20458     },
20459
20460     /**
20461      * Selects nodes.
20462      * @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
20463      * @param {Boolean} keepExisting (optional) true to keep existing selections
20464      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20465      */
20466     select : function(nodeInfo, keepExisting, suppressEvent){
20467         if(nodeInfo instanceof Array){
20468             if(!keepExisting){
20469                 this.clearSelections(true);
20470             }
20471             for(var i = 0, len = nodeInfo.length; i < len; i++){
20472                 this.select(nodeInfo[i], true, true);
20473             }
20474             return;
20475         } 
20476         var node = this.getNode(nodeInfo);
20477         if(!node || this.isSelected(node)){
20478             return; // already selected.
20479         }
20480         if(!keepExisting){
20481             this.clearSelections(true);
20482         }
20483         
20484         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20485             Roo.fly(node).addClass(this.selectedClass);
20486             this.selections.push(node);
20487             if(!suppressEvent){
20488                 this.fireEvent("selectionchange", this, this.selections);
20489             }
20490         }
20491         
20492         
20493     },
20494       /**
20495      * Unselects nodes.
20496      * @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
20497      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20498      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20499      */
20500     unselect : function(nodeInfo, keepExisting, suppressEvent)
20501     {
20502         if(nodeInfo instanceof Array){
20503             Roo.each(this.selections, function(s) {
20504                 this.unselect(s, nodeInfo);
20505             }, this);
20506             return;
20507         }
20508         var node = this.getNode(nodeInfo);
20509         if(!node || !this.isSelected(node)){
20510             //Roo.log("not selected");
20511             return; // not selected.
20512         }
20513         // fireevent???
20514         var ns = [];
20515         Roo.each(this.selections, function(s) {
20516             if (s == node ) {
20517                 Roo.fly(node).removeClass(this.selectedClass);
20518
20519                 return;
20520             }
20521             ns.push(s);
20522         },this);
20523         
20524         this.selections= ns;
20525         this.fireEvent("selectionchange", this, this.selections);
20526     },
20527
20528     /**
20529      * Gets a template node.
20530      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20531      * @return {HTMLElement} The node or null if it wasn't found
20532      */
20533     getNode : function(nodeInfo){
20534         if(typeof nodeInfo == "string"){
20535             return document.getElementById(nodeInfo);
20536         }else if(typeof nodeInfo == "number"){
20537             return this.nodes[nodeInfo];
20538         }
20539         return nodeInfo;
20540     },
20541
20542     /**
20543      * Gets a range template nodes.
20544      * @param {Number} startIndex
20545      * @param {Number} endIndex
20546      * @return {Array} An array of nodes
20547      */
20548     getNodes : function(start, end){
20549         var ns = this.nodes;
20550         start = start || 0;
20551         end = typeof end == "undefined" ? ns.length - 1 : end;
20552         var nodes = [];
20553         if(start <= end){
20554             for(var i = start; i <= end; i++){
20555                 nodes.push(ns[i]);
20556             }
20557         } else{
20558             for(var i = start; i >= end; i--){
20559                 nodes.push(ns[i]);
20560             }
20561         }
20562         return nodes;
20563     },
20564
20565     /**
20566      * Finds the index of the passed node
20567      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20568      * @return {Number} The index of the node or -1
20569      */
20570     indexOf : function(node){
20571         node = this.getNode(node);
20572         if(typeof node.nodeIndex == "number"){
20573             return node.nodeIndex;
20574         }
20575         var ns = this.nodes;
20576         for(var i = 0, len = ns.length; i < len; i++){
20577             if(ns[i] == node){
20578                 return i;
20579             }
20580         }
20581         return -1;
20582     }
20583 });
20584 /*
20585  * - LGPL
20586  *
20587  * based on jquery fullcalendar
20588  * 
20589  */
20590
20591 Roo.bootstrap = Roo.bootstrap || {};
20592 /**
20593  * @class Roo.bootstrap.Calendar
20594  * @extends Roo.bootstrap.Component
20595  * Bootstrap Calendar class
20596  * @cfg {Boolean} loadMask (true|false) default false
20597  * @cfg {Object} header generate the user specific header of the calendar, default false
20598
20599  * @constructor
20600  * Create a new Container
20601  * @param {Object} config The config object
20602  */
20603
20604
20605
20606 Roo.bootstrap.Calendar = function(config){
20607     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20608      this.addEvents({
20609         /**
20610              * @event select
20611              * Fires when a date is selected
20612              * @param {DatePicker} this
20613              * @param {Date} date The selected date
20614              */
20615         'select': true,
20616         /**
20617              * @event monthchange
20618              * Fires when the displayed month changes 
20619              * @param {DatePicker} this
20620              * @param {Date} date The selected month
20621              */
20622         'monthchange': true,
20623         /**
20624              * @event evententer
20625              * Fires when mouse over an event
20626              * @param {Calendar} this
20627              * @param {event} Event
20628              */
20629         'evententer': true,
20630         /**
20631              * @event eventleave
20632              * Fires when the mouse leaves an
20633              * @param {Calendar} this
20634              * @param {event}
20635              */
20636         'eventleave': true,
20637         /**
20638              * @event eventclick
20639              * Fires when the mouse click an
20640              * @param {Calendar} this
20641              * @param {event}
20642              */
20643         'eventclick': true
20644         
20645     });
20646
20647 };
20648
20649 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20650     
20651           /**
20652      * @cfg {Roo.data.Store} store
20653      * The data source for the calendar
20654      */
20655         store : false,
20656      /**
20657      * @cfg {Number} startDay
20658      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20659      */
20660     startDay : 0,
20661     
20662     loadMask : false,
20663     
20664     header : false,
20665       
20666     getAutoCreate : function(){
20667         
20668         
20669         var fc_button = function(name, corner, style, content ) {
20670             return Roo.apply({},{
20671                 tag : 'span',
20672                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20673                          (corner.length ?
20674                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20675                             ''
20676                         ),
20677                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20678                 unselectable: 'on'
20679             });
20680         };
20681         
20682         var header = {};
20683         
20684         if(!this.header){
20685             header = {
20686                 tag : 'table',
20687                 cls : 'fc-header',
20688                 style : 'width:100%',
20689                 cn : [
20690                     {
20691                         tag: 'tr',
20692                         cn : [
20693                             {
20694                                 tag : 'td',
20695                                 cls : 'fc-header-left',
20696                                 cn : [
20697                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20698                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20699                                     { tag: 'span', cls: 'fc-header-space' },
20700                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20701
20702
20703                                 ]
20704                             },
20705
20706                             {
20707                                 tag : 'td',
20708                                 cls : 'fc-header-center',
20709                                 cn : [
20710                                     {
20711                                         tag: 'span',
20712                                         cls: 'fc-header-title',
20713                                         cn : {
20714                                             tag: 'H2',
20715                                             html : 'month / year'
20716                                         }
20717                                     }
20718
20719                                 ]
20720                             },
20721                             {
20722                                 tag : 'td',
20723                                 cls : 'fc-header-right',
20724                                 cn : [
20725                               /*      fc_button('month', 'left', '', 'month' ),
20726                                     fc_button('week', '', '', 'week' ),
20727                                     fc_button('day', 'right', '', 'day' )
20728                                 */    
20729
20730                                 ]
20731                             }
20732
20733                         ]
20734                     }
20735                 ]
20736             };
20737         }
20738         
20739         header = this.header;
20740         
20741        
20742         var cal_heads = function() {
20743             var ret = [];
20744             // fixme - handle this.
20745             
20746             for (var i =0; i < Date.dayNames.length; i++) {
20747                 var d = Date.dayNames[i];
20748                 ret.push({
20749                     tag: 'th',
20750                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20751                     html : d.substring(0,3)
20752                 });
20753                 
20754             }
20755             ret[0].cls += ' fc-first';
20756             ret[6].cls += ' fc-last';
20757             return ret;
20758         };
20759         var cal_cell = function(n) {
20760             return  {
20761                 tag: 'td',
20762                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20763                 cn : [
20764                     {
20765                         cn : [
20766                             {
20767                                 cls: 'fc-day-number',
20768                                 html: 'D'
20769                             },
20770                             {
20771                                 cls: 'fc-day-content',
20772                              
20773                                 cn : [
20774                                      {
20775                                         style: 'position: relative;' // height: 17px;
20776                                     }
20777                                 ]
20778                             }
20779                             
20780                             
20781                         ]
20782                     }
20783                 ]
20784                 
20785             }
20786         };
20787         var cal_rows = function() {
20788             
20789             var ret = [];
20790             for (var r = 0; r < 6; r++) {
20791                 var row= {
20792                     tag : 'tr',
20793                     cls : 'fc-week',
20794                     cn : []
20795                 };
20796                 
20797                 for (var i =0; i < Date.dayNames.length; i++) {
20798                     var d = Date.dayNames[i];
20799                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20800
20801                 }
20802                 row.cn[0].cls+=' fc-first';
20803                 row.cn[0].cn[0].style = 'min-height:90px';
20804                 row.cn[6].cls+=' fc-last';
20805                 ret.push(row);
20806                 
20807             }
20808             ret[0].cls += ' fc-first';
20809             ret[4].cls += ' fc-prev-last';
20810             ret[5].cls += ' fc-last';
20811             return ret;
20812             
20813         };
20814         
20815         var cal_table = {
20816             tag: 'table',
20817             cls: 'fc-border-separate',
20818             style : 'width:100%',
20819             cellspacing  : 0,
20820             cn : [
20821                 { 
20822                     tag: 'thead',
20823                     cn : [
20824                         { 
20825                             tag: 'tr',
20826                             cls : 'fc-first fc-last',
20827                             cn : cal_heads()
20828                         }
20829                     ]
20830                 },
20831                 { 
20832                     tag: 'tbody',
20833                     cn : cal_rows()
20834                 }
20835                   
20836             ]
20837         };
20838          
20839          var cfg = {
20840             cls : 'fc fc-ltr',
20841             cn : [
20842                 header,
20843                 {
20844                     cls : 'fc-content',
20845                     style : "position: relative;",
20846                     cn : [
20847                         {
20848                             cls : 'fc-view fc-view-month fc-grid',
20849                             style : 'position: relative',
20850                             unselectable : 'on',
20851                             cn : [
20852                                 {
20853                                     cls : 'fc-event-container',
20854                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20855                                 },
20856                                 cal_table
20857                             ]
20858                         }
20859                     ]
20860     
20861                 }
20862            ] 
20863             
20864         };
20865         
20866          
20867         
20868         return cfg;
20869     },
20870     
20871     
20872     initEvents : function()
20873     {
20874         if(!this.store){
20875             throw "can not find store for calendar";
20876         }
20877         
20878         var mark = {
20879             tag: "div",
20880             cls:"x-dlg-mask",
20881             style: "text-align:center",
20882             cn: [
20883                 {
20884                     tag: "div",
20885                     style: "background-color:white;width:50%;margin:250 auto",
20886                     cn: [
20887                         {
20888                             tag: "img",
20889                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20890                         },
20891                         {
20892                             tag: "span",
20893                             html: "Loading"
20894                         }
20895                         
20896                     ]
20897                 }
20898             ]
20899         };
20900         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20901         
20902         var size = this.el.select('.fc-content', true).first().getSize();
20903         this.maskEl.setSize(size.width, size.height);
20904         this.maskEl.enableDisplayMode("block");
20905         if(!this.loadMask){
20906             this.maskEl.hide();
20907         }
20908         
20909         this.store = Roo.factory(this.store, Roo.data);
20910         this.store.on('load', this.onLoad, this);
20911         this.store.on('beforeload', this.onBeforeLoad, this);
20912         
20913         this.resize();
20914         
20915         this.cells = this.el.select('.fc-day',true);
20916         //Roo.log(this.cells);
20917         this.textNodes = this.el.query('.fc-day-number');
20918         this.cells.addClassOnOver('fc-state-hover');
20919         
20920         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20921         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20922         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20923         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20924         
20925         this.on('monthchange', this.onMonthChange, this);
20926         
20927         this.update(new Date().clearTime());
20928     },
20929     
20930     resize : function() {
20931         var sz  = this.el.getSize();
20932         
20933         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20934         this.el.select('.fc-day-content div',true).setHeight(34);
20935     },
20936     
20937     
20938     // private
20939     showPrevMonth : function(e){
20940         this.update(this.activeDate.add("mo", -1));
20941     },
20942     showToday : function(e){
20943         this.update(new Date().clearTime());
20944     },
20945     // private
20946     showNextMonth : function(e){
20947         this.update(this.activeDate.add("mo", 1));
20948     },
20949
20950     // private
20951     showPrevYear : function(){
20952         this.update(this.activeDate.add("y", -1));
20953     },
20954
20955     // private
20956     showNextYear : function(){
20957         this.update(this.activeDate.add("y", 1));
20958     },
20959
20960     
20961    // private
20962     update : function(date)
20963     {
20964         var vd = this.activeDate;
20965         this.activeDate = date;
20966 //        if(vd && this.el){
20967 //            var t = date.getTime();
20968 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20969 //                Roo.log('using add remove');
20970 //                
20971 //                this.fireEvent('monthchange', this, date);
20972 //                
20973 //                this.cells.removeClass("fc-state-highlight");
20974 //                this.cells.each(function(c){
20975 //                   if(c.dateValue == t){
20976 //                       c.addClass("fc-state-highlight");
20977 //                       setTimeout(function(){
20978 //                            try{c.dom.firstChild.focus();}catch(e){}
20979 //                       }, 50);
20980 //                       return false;
20981 //                   }
20982 //                   return true;
20983 //                });
20984 //                return;
20985 //            }
20986 //        }
20987         
20988         var days = date.getDaysInMonth();
20989         
20990         var firstOfMonth = date.getFirstDateOfMonth();
20991         var startingPos = firstOfMonth.getDay()-this.startDay;
20992         
20993         if(startingPos < this.startDay){
20994             startingPos += 7;
20995         }
20996         
20997         var pm = date.add(Date.MONTH, -1);
20998         var prevStart = pm.getDaysInMonth()-startingPos;
20999 //        
21000         this.cells = this.el.select('.fc-day',true);
21001         this.textNodes = this.el.query('.fc-day-number');
21002         this.cells.addClassOnOver('fc-state-hover');
21003         
21004         var cells = this.cells.elements;
21005         var textEls = this.textNodes;
21006         
21007         Roo.each(cells, function(cell){
21008             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21009         });
21010         
21011         days += startingPos;
21012
21013         // convert everything to numbers so it's fast
21014         var day = 86400000;
21015         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21016         //Roo.log(d);
21017         //Roo.log(pm);
21018         //Roo.log(prevStart);
21019         
21020         var today = new Date().clearTime().getTime();
21021         var sel = date.clearTime().getTime();
21022         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21023         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21024         var ddMatch = this.disabledDatesRE;
21025         var ddText = this.disabledDatesText;
21026         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21027         var ddaysText = this.disabledDaysText;
21028         var format = this.format;
21029         
21030         var setCellClass = function(cal, cell){
21031             cell.row = 0;
21032             cell.events = [];
21033             cell.more = [];
21034             //Roo.log('set Cell Class');
21035             cell.title = "";
21036             var t = d.getTime();
21037             
21038             //Roo.log(d);
21039             
21040             cell.dateValue = t;
21041             if(t == today){
21042                 cell.className += " fc-today";
21043                 cell.className += " fc-state-highlight";
21044                 cell.title = cal.todayText;
21045             }
21046             if(t == sel){
21047                 // disable highlight in other month..
21048                 //cell.className += " fc-state-highlight";
21049                 
21050             }
21051             // disabling
21052             if(t < min) {
21053                 cell.className = " fc-state-disabled";
21054                 cell.title = cal.minText;
21055                 return;
21056             }
21057             if(t > max) {
21058                 cell.className = " fc-state-disabled";
21059                 cell.title = cal.maxText;
21060                 return;
21061             }
21062             if(ddays){
21063                 if(ddays.indexOf(d.getDay()) != -1){
21064                     cell.title = ddaysText;
21065                     cell.className = " fc-state-disabled";
21066                 }
21067             }
21068             if(ddMatch && format){
21069                 var fvalue = d.dateFormat(format);
21070                 if(ddMatch.test(fvalue)){
21071                     cell.title = ddText.replace("%0", fvalue);
21072                     cell.className = " fc-state-disabled";
21073                 }
21074             }
21075             
21076             if (!cell.initialClassName) {
21077                 cell.initialClassName = cell.dom.className;
21078             }
21079             
21080             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21081         };
21082
21083         var i = 0;
21084         
21085         for(; i < startingPos; i++) {
21086             textEls[i].innerHTML = (++prevStart);
21087             d.setDate(d.getDate()+1);
21088             
21089             cells[i].className = "fc-past fc-other-month";
21090             setCellClass(this, cells[i]);
21091         }
21092         
21093         var intDay = 0;
21094         
21095         for(; i < days; i++){
21096             intDay = i - startingPos + 1;
21097             textEls[i].innerHTML = (intDay);
21098             d.setDate(d.getDate()+1);
21099             
21100             cells[i].className = ''; // "x-date-active";
21101             setCellClass(this, cells[i]);
21102         }
21103         var extraDays = 0;
21104         
21105         for(; i < 42; i++) {
21106             textEls[i].innerHTML = (++extraDays);
21107             d.setDate(d.getDate()+1);
21108             
21109             cells[i].className = "fc-future fc-other-month";
21110             setCellClass(this, cells[i]);
21111         }
21112         
21113         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21114         
21115         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21116         
21117         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21118         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21119         
21120         if(totalRows != 6){
21121             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21122             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21123         }
21124         
21125         this.fireEvent('monthchange', this, date);
21126         
21127         
21128         /*
21129         if(!this.internalRender){
21130             var main = this.el.dom.firstChild;
21131             var w = main.offsetWidth;
21132             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21133             Roo.fly(main).setWidth(w);
21134             this.internalRender = true;
21135             // opera does not respect the auto grow header center column
21136             // then, after it gets a width opera refuses to recalculate
21137             // without a second pass
21138             if(Roo.isOpera && !this.secondPass){
21139                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21140                 this.secondPass = true;
21141                 this.update.defer(10, this, [date]);
21142             }
21143         }
21144         */
21145         
21146     },
21147     
21148     findCell : function(dt) {
21149         dt = dt.clearTime().getTime();
21150         var ret = false;
21151         this.cells.each(function(c){
21152             //Roo.log("check " +c.dateValue + '?=' + dt);
21153             if(c.dateValue == dt){
21154                 ret = c;
21155                 return false;
21156             }
21157             return true;
21158         });
21159         
21160         return ret;
21161     },
21162     
21163     findCells : function(ev) {
21164         var s = ev.start.clone().clearTime().getTime();
21165        // Roo.log(s);
21166         var e= ev.end.clone().clearTime().getTime();
21167        // Roo.log(e);
21168         var ret = [];
21169         this.cells.each(function(c){
21170              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21171             
21172             if(c.dateValue > e){
21173                 return ;
21174             }
21175             if(c.dateValue < s){
21176                 return ;
21177             }
21178             ret.push(c);
21179         });
21180         
21181         return ret;    
21182     },
21183     
21184 //    findBestRow: function(cells)
21185 //    {
21186 //        var ret = 0;
21187 //        
21188 //        for (var i =0 ; i < cells.length;i++) {
21189 //            ret  = Math.max(cells[i].rows || 0,ret);
21190 //        }
21191 //        return ret;
21192 //        
21193 //    },
21194     
21195     
21196     addItem : function(ev)
21197     {
21198         // look for vertical location slot in
21199         var cells = this.findCells(ev);
21200         
21201 //        ev.row = this.findBestRow(cells);
21202         
21203         // work out the location.
21204         
21205         var crow = false;
21206         var rows = [];
21207         for(var i =0; i < cells.length; i++) {
21208             
21209             cells[i].row = cells[0].row;
21210             
21211             if(i == 0){
21212                 cells[i].row = cells[i].row + 1;
21213             }
21214             
21215             if (!crow) {
21216                 crow = {
21217                     start : cells[i],
21218                     end :  cells[i]
21219                 };
21220                 continue;
21221             }
21222             if (crow.start.getY() == cells[i].getY()) {
21223                 // on same row.
21224                 crow.end = cells[i];
21225                 continue;
21226             }
21227             // different row.
21228             rows.push(crow);
21229             crow = {
21230                 start: cells[i],
21231                 end : cells[i]
21232             };
21233             
21234         }
21235         
21236         rows.push(crow);
21237         ev.els = [];
21238         ev.rows = rows;
21239         ev.cells = cells;
21240         
21241         cells[0].events.push(ev);
21242         
21243         this.calevents.push(ev);
21244     },
21245     
21246     clearEvents: function() {
21247         
21248         if(!this.calevents){
21249             return;
21250         }
21251         
21252         Roo.each(this.cells.elements, function(c){
21253             c.row = 0;
21254             c.events = [];
21255             c.more = [];
21256         });
21257         
21258         Roo.each(this.calevents, function(e) {
21259             Roo.each(e.els, function(el) {
21260                 el.un('mouseenter' ,this.onEventEnter, this);
21261                 el.un('mouseleave' ,this.onEventLeave, this);
21262                 el.remove();
21263             },this);
21264         },this);
21265         
21266         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21267             e.remove();
21268         });
21269         
21270     },
21271     
21272     renderEvents: function()
21273     {   
21274         var _this = this;
21275         
21276         this.cells.each(function(c) {
21277             
21278             if(c.row < 5){
21279                 return;
21280             }
21281             
21282             var ev = c.events;
21283             
21284             var r = 4;
21285             if(c.row != c.events.length){
21286                 r = 4 - (4 - (c.row - c.events.length));
21287             }
21288             
21289             c.events = ev.slice(0, r);
21290             c.more = ev.slice(r);
21291             
21292             if(c.more.length && c.more.length == 1){
21293                 c.events.push(c.more.pop());
21294             }
21295             
21296             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21297             
21298         });
21299             
21300         this.cells.each(function(c) {
21301             
21302             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21303             
21304             
21305             for (var e = 0; e < c.events.length; e++){
21306                 var ev = c.events[e];
21307                 var rows = ev.rows;
21308                 
21309                 for(var i = 0; i < rows.length; i++) {
21310                 
21311                     // how many rows should it span..
21312
21313                     var  cfg = {
21314                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21315                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21316
21317                         unselectable : "on",
21318                         cn : [
21319                             {
21320                                 cls: 'fc-event-inner',
21321                                 cn : [
21322     //                                {
21323     //                                  tag:'span',
21324     //                                  cls: 'fc-event-time',
21325     //                                  html : cells.length > 1 ? '' : ev.time
21326     //                                },
21327                                     {
21328                                       tag:'span',
21329                                       cls: 'fc-event-title',
21330                                       html : String.format('{0}', ev.title)
21331                                     }
21332
21333
21334                                 ]
21335                             },
21336                             {
21337                                 cls: 'ui-resizable-handle ui-resizable-e',
21338                                 html : '&nbsp;&nbsp;&nbsp'
21339                             }
21340
21341                         ]
21342                     };
21343
21344                     if (i == 0) {
21345                         cfg.cls += ' fc-event-start';
21346                     }
21347                     if ((i+1) == rows.length) {
21348                         cfg.cls += ' fc-event-end';
21349                     }
21350
21351                     var ctr = _this.el.select('.fc-event-container',true).first();
21352                     var cg = ctr.createChild(cfg);
21353
21354                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21355                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21356
21357                     var r = (c.more.length) ? 1 : 0;
21358                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21359                     cg.setWidth(ebox.right - sbox.x -2);
21360
21361                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21362                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21363                     cg.on('click', _this.onEventClick, _this, ev);
21364
21365                     ev.els.push(cg);
21366                     
21367                 }
21368                 
21369             }
21370             
21371             
21372             if(c.more.length){
21373                 var  cfg = {
21374                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21375                     style : 'position: absolute',
21376                     unselectable : "on",
21377                     cn : [
21378                         {
21379                             cls: 'fc-event-inner',
21380                             cn : [
21381                                 {
21382                                   tag:'span',
21383                                   cls: 'fc-event-title',
21384                                   html : 'More'
21385                                 }
21386
21387
21388                             ]
21389                         },
21390                         {
21391                             cls: 'ui-resizable-handle ui-resizable-e',
21392                             html : '&nbsp;&nbsp;&nbsp'
21393                         }
21394
21395                     ]
21396                 };
21397
21398                 var ctr = _this.el.select('.fc-event-container',true).first();
21399                 var cg = ctr.createChild(cfg);
21400
21401                 var sbox = c.select('.fc-day-content',true).first().getBox();
21402                 var ebox = c.select('.fc-day-content',true).first().getBox();
21403                 //Roo.log(cg);
21404                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21405                 cg.setWidth(ebox.right - sbox.x -2);
21406
21407                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21408                 
21409             }
21410             
21411         });
21412         
21413         
21414         
21415     },
21416     
21417     onEventEnter: function (e, el,event,d) {
21418         this.fireEvent('evententer', this, el, event);
21419     },
21420     
21421     onEventLeave: function (e, el,event,d) {
21422         this.fireEvent('eventleave', this, el, event);
21423     },
21424     
21425     onEventClick: function (e, el,event,d) {
21426         this.fireEvent('eventclick', this, el, event);
21427     },
21428     
21429     onMonthChange: function () {
21430         this.store.load();
21431     },
21432     
21433     onMoreEventClick: function(e, el, more)
21434     {
21435         var _this = this;
21436         
21437         this.calpopover.placement = 'right';
21438         this.calpopover.setTitle('More');
21439         
21440         this.calpopover.setContent('');
21441         
21442         var ctr = this.calpopover.el.select('.popover-content', true).first();
21443         
21444         Roo.each(more, function(m){
21445             var cfg = {
21446                 cls : 'fc-event-hori fc-event-draggable',
21447                 html : m.title
21448             };
21449             var cg = ctr.createChild(cfg);
21450             
21451             cg.on('click', _this.onEventClick, _this, m);
21452         });
21453         
21454         this.calpopover.show(el);
21455         
21456         
21457     },
21458     
21459     onLoad: function () 
21460     {   
21461         this.calevents = [];
21462         var cal = this;
21463         
21464         if(this.store.getCount() > 0){
21465             this.store.data.each(function(d){
21466                cal.addItem({
21467                     id : d.data.id,
21468                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21469                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21470                     time : d.data.start_time,
21471                     title : d.data.title,
21472                     description : d.data.description,
21473                     venue : d.data.venue
21474                 });
21475             });
21476         }
21477         
21478         this.renderEvents();
21479         
21480         if(this.calevents.length && this.loadMask){
21481             this.maskEl.hide();
21482         }
21483     },
21484     
21485     onBeforeLoad: function()
21486     {
21487         this.clearEvents();
21488         if(this.loadMask){
21489             this.maskEl.show();
21490         }
21491     }
21492 });
21493
21494  
21495  /*
21496  * - LGPL
21497  *
21498  * element
21499  * 
21500  */
21501
21502 /**
21503  * @class Roo.bootstrap.Popover
21504  * @extends Roo.bootstrap.Component
21505  * @parent none builder
21506  * @children Roo.bootstrap.Component
21507  * Bootstrap Popover class
21508  * @cfg {String} html contents of the popover   (or false to use children..)
21509  * @cfg {String} title of popover (or false to hide)
21510  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21511  * @cfg {String} trigger click || hover (or false to trigger manually)
21512  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21513  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21514  *      - if false and it has a 'parent' then it will be automatically added to that element
21515  *      - if string - Roo.get  will be called 
21516  * @cfg {Number} delay - delay before showing
21517  
21518  * @constructor
21519  * Create a new Popover
21520  * @param {Object} config The config object
21521  */
21522
21523 Roo.bootstrap.Popover = function(config){
21524     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21525     
21526     this.addEvents({
21527         // raw events
21528          /**
21529          * @event show
21530          * After the popover show
21531          * 
21532          * @param {Roo.bootstrap.Popover} this
21533          */
21534         "show" : true,
21535         /**
21536          * @event hide
21537          * After the popover hide
21538          * 
21539          * @param {Roo.bootstrap.Popover} this
21540          */
21541         "hide" : true
21542     });
21543 };
21544
21545 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21546     
21547     title: false,
21548     html: false,
21549     
21550     placement : 'right',
21551     trigger : 'hover', // hover
21552     modal : false,
21553     delay : 0,
21554     
21555     over: false,
21556     
21557     can_build_overlaid : false,
21558     
21559     maskEl : false, // the mask element
21560     headerEl : false,
21561     contentEl : false,
21562     alignEl : false, // when show is called with an element - this get's stored.
21563     
21564     getChildContainer : function()
21565     {
21566         return this.contentEl;
21567         
21568     },
21569     getPopoverHeader : function()
21570     {
21571         this.title = true; // flag not to hide it..
21572         this.headerEl.addClass('p-0');
21573         return this.headerEl
21574     },
21575     
21576     
21577     getAutoCreate : function(){
21578          
21579         var cfg = {
21580            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21581            style: 'display:block',
21582            cn : [
21583                 {
21584                     cls : 'arrow'
21585                 },
21586                 {
21587                     cls : 'popover-inner ',
21588                     cn : [
21589                         {
21590                             tag: 'h3',
21591                             cls: 'popover-title popover-header',
21592                             html : this.title === false ? '' : this.title
21593                         },
21594                         {
21595                             cls : 'popover-content popover-body '  + (this.cls || ''),
21596                             html : this.html || ''
21597                         }
21598                     ]
21599                     
21600                 }
21601            ]
21602         };
21603         
21604         return cfg;
21605     },
21606     /**
21607      * @param {string} the title
21608      */
21609     setTitle: function(str)
21610     {
21611         this.title = str;
21612         if (this.el) {
21613             this.headerEl.dom.innerHTML = str;
21614         }
21615         
21616     },
21617     /**
21618      * @param {string} the body content
21619      */
21620     setContent: function(str)
21621     {
21622         this.html = str;
21623         if (this.contentEl) {
21624             this.contentEl.dom.innerHTML = str;
21625         }
21626         
21627     },
21628     // as it get's added to the bottom of the page.
21629     onRender : function(ct, position)
21630     {
21631         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21632         
21633         
21634         
21635         if(!this.el){
21636             var cfg = Roo.apply({},  this.getAutoCreate());
21637             cfg.id = Roo.id();
21638             
21639             if (this.cls) {
21640                 cfg.cls += ' ' + this.cls;
21641             }
21642             if (this.style) {
21643                 cfg.style = this.style;
21644             }
21645             //Roo.log("adding to ");
21646             this.el = Roo.get(document.body).createChild(cfg, position);
21647 //            Roo.log(this.el);
21648         }
21649         
21650         this.contentEl = this.el.select('.popover-content',true).first();
21651         this.headerEl =  this.el.select('.popover-title',true).first();
21652         
21653         var nitems = [];
21654         if(typeof(this.items) != 'undefined'){
21655             var items = this.items;
21656             delete this.items;
21657
21658             for(var i =0;i < items.length;i++) {
21659                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21660             }
21661         }
21662
21663         this.items = nitems;
21664         
21665         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21666         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21667         
21668         
21669         
21670         this.initEvents();
21671     },
21672     
21673     resizeMask : function()
21674     {
21675         this.maskEl.setSize(
21676             Roo.lib.Dom.getViewWidth(true),
21677             Roo.lib.Dom.getViewHeight(true)
21678         );
21679     },
21680     
21681     initEvents : function()
21682     {
21683         
21684         if (!this.modal) { 
21685             Roo.bootstrap.Popover.register(this);
21686         }
21687          
21688         this.arrowEl = this.el.select('.arrow',true).first();
21689         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21690         this.el.enableDisplayMode('block');
21691         this.el.hide();
21692  
21693         
21694         if (this.over === false && !this.parent()) {
21695             return; 
21696         }
21697         if (this.triggers === false) {
21698             return;
21699         }
21700          
21701         // support parent
21702         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21703         var triggers = this.trigger ? this.trigger.split(' ') : [];
21704         Roo.each(triggers, function(trigger) {
21705         
21706             if (trigger == 'click') {
21707                 on_el.on('click', this.toggle, this);
21708             } else if (trigger != 'manual') {
21709                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21710                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21711       
21712                 on_el.on(eventIn  ,this.enter, this);
21713                 on_el.on(eventOut, this.leave, this);
21714             }
21715         }, this);
21716     },
21717     
21718     
21719     // private
21720     timeout : null,
21721     hoverState : null,
21722     
21723     toggle : function () {
21724         this.hoverState == 'in' ? this.leave() : this.enter();
21725     },
21726     
21727     enter : function () {
21728         
21729         clearTimeout(this.timeout);
21730     
21731         this.hoverState = 'in';
21732     
21733         if (!this.delay || !this.delay.show) {
21734             this.show();
21735             return;
21736         }
21737         var _t = this;
21738         this.timeout = setTimeout(function () {
21739             if (_t.hoverState == 'in') {
21740                 _t.show();
21741             }
21742         }, this.delay.show)
21743     },
21744     
21745     leave : function() {
21746         clearTimeout(this.timeout);
21747     
21748         this.hoverState = 'out';
21749     
21750         if (!this.delay || !this.delay.hide) {
21751             this.hide();
21752             return;
21753         }
21754         var _t = this;
21755         this.timeout = setTimeout(function () {
21756             if (_t.hoverState == 'out') {
21757                 _t.hide();
21758             }
21759         }, this.delay.hide)
21760     },
21761     
21762     /**
21763      * update the position of the dialog
21764      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21765      * 
21766      *
21767      */
21768     
21769     doAlign : function()
21770     {
21771         
21772         if (this.alignEl) {
21773             this.updatePosition(this.placement, true);
21774              
21775         } else {
21776             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21777             var es = this.el.getSize();
21778             var x = Roo.lib.Dom.getViewWidth()/2;
21779             var y = Roo.lib.Dom.getViewHeight()/2;
21780             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21781             
21782         }
21783
21784          
21785          
21786         
21787         
21788     },
21789     
21790     /**
21791      * Show the popover
21792      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21793      * @param {string} (left|right|top|bottom) position
21794      */
21795     show : function (on_el, placement)
21796     {
21797         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21798         on_el = on_el || false; // default to false
21799          
21800         if (!on_el) {
21801             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21802                 on_el = this.parent().el;
21803             } else if (this.over) {
21804                 on_el = Roo.get(this.over);
21805             }
21806             
21807         }
21808         
21809         this.alignEl = Roo.get( on_el );
21810
21811         if (!this.el) {
21812             this.render(document.body);
21813         }
21814         
21815         
21816          
21817         
21818         if (this.title === false) {
21819             this.headerEl.hide();
21820         }
21821         
21822        
21823         this.el.show();
21824         this.el.dom.style.display = 'block';
21825          
21826         this.doAlign();
21827         
21828         //var arrow = this.el.select('.arrow',true).first();
21829         //arrow.set(align[2], 
21830         
21831         this.el.addClass('in');
21832         
21833          
21834         
21835         this.hoverState = 'in';
21836         
21837         if (this.modal) {
21838             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21839             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21840             this.maskEl.dom.style.display = 'block';
21841             this.maskEl.addClass('show');
21842         }
21843         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21844  
21845         this.fireEvent('show', this);
21846         
21847     },
21848     /**
21849      * fire this manually after loading a grid in the table for example
21850      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21851      * @param {Boolean} try and move it if we cant get right position.
21852      */
21853     updatePosition : function(placement, try_move)
21854     {
21855         // allow for calling with no parameters
21856         placement = placement   ? placement :  this.placement;
21857         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21858         
21859         this.el.removeClass([
21860             'fade','top','bottom', 'left', 'right','in',
21861             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21862         ]);
21863         this.el.addClass(placement + ' bs-popover-' + placement);
21864         
21865         if (!this.alignEl ) {
21866             return false;
21867         }
21868         
21869         switch (placement) {
21870             case 'right':
21871                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21872                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21873                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21874                     //normal display... or moved up/down.
21875                     this.el.setXY(offset);
21876                     var xy = this.alignEl.getAnchorXY('tr', false);
21877                     xy[0]+=2;xy[1]+=5;
21878                     this.arrowEl.setXY(xy);
21879                     return true;
21880                 }
21881                 // continue through...
21882                 return this.updatePosition('left', false);
21883                 
21884             
21885             case 'left':
21886                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21887                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21888                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21889                     //normal display... or moved up/down.
21890                     this.el.setXY(offset);
21891                     var xy = this.alignEl.getAnchorXY('tl', false);
21892                     xy[0]-=10;xy[1]+=5; // << fix me
21893                     this.arrowEl.setXY(xy);
21894                     return true;
21895                 }
21896                 // call self...
21897                 return this.updatePosition('right', false);
21898             
21899             case 'top':
21900                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21901                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21902                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21903                     //normal display... or moved up/down.
21904                     this.el.setXY(offset);
21905                     var xy = this.alignEl.getAnchorXY('t', false);
21906                     xy[1]-=10; // << fix me
21907                     this.arrowEl.setXY(xy);
21908                     return true;
21909                 }
21910                 // fall through
21911                return this.updatePosition('bottom', false);
21912             
21913             case 'bottom':
21914                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21915                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21916                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21917                     //normal display... or moved up/down.
21918                     this.el.setXY(offset);
21919                     var xy = this.alignEl.getAnchorXY('b', false);
21920                      xy[1]+=2; // << fix me
21921                     this.arrowEl.setXY(xy);
21922                     return true;
21923                 }
21924                 // fall through
21925                 return this.updatePosition('top', false);
21926                 
21927             
21928         }
21929         
21930         
21931         return false;
21932     },
21933     
21934     hide : function()
21935     {
21936         this.el.setXY([0,0]);
21937         this.el.removeClass('in');
21938         this.el.hide();
21939         this.hoverState = null;
21940         this.maskEl.hide(); // always..
21941         this.fireEvent('hide', this);
21942     }
21943     
21944 });
21945
21946
21947 Roo.apply(Roo.bootstrap.Popover, {
21948
21949     alignment : {
21950         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21951         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21952         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21953         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21954     },
21955     
21956     zIndex : 20001,
21957
21958     clickHander : false,
21959     
21960     
21961
21962     onMouseDown : function(e)
21963     {
21964         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21965             /// what is nothing is showing..
21966             this.hideAll();
21967         }
21968          
21969     },
21970     
21971     
21972     popups : [],
21973     
21974     register : function(popup)
21975     {
21976         if (!Roo.bootstrap.Popover.clickHandler) {
21977             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21978         }
21979         // hide other popups.
21980         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21981         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21982         this.hideAll(); //<< why?
21983         //this.popups.push(popup);
21984     },
21985     hideAll : function()
21986     {
21987         this.popups.forEach(function(p) {
21988             p.hide();
21989         });
21990     },
21991     onShow : function() {
21992         Roo.bootstrap.Popover.popups.push(this);
21993     },
21994     onHide : function() {
21995         Roo.bootstrap.Popover.popups.remove(this);
21996     } 
21997
21998 });
21999 /**
22000  * @class Roo.bootstrap.PopoverNav
22001  * @extends Roo.bootstrap.nav.Simplebar
22002  * @parent Roo.bootstrap.Popover
22003  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22004  * @licence LGPL
22005  * Bootstrap Popover header navigation class
22006  * FIXME? should this go under nav?
22007  *
22008  * 
22009  * @constructor
22010  * Create a new Popover Header Navigation 
22011  * @param {Object} config The config object
22012  */
22013
22014 Roo.bootstrap.PopoverNav = function(config){
22015     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22016 };
22017
22018 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22019     
22020     
22021     container_method : 'getPopoverHeader' 
22022     
22023      
22024     
22025     
22026    
22027 });
22028
22029  
22030
22031  /*
22032  * - LGPL
22033  *
22034  * Progress
22035  * 
22036  */
22037
22038 /**
22039  * @class Roo.bootstrap.Progress
22040  * @extends Roo.bootstrap.Component
22041  * @children Roo.bootstrap.ProgressBar
22042  * Bootstrap Progress class
22043  * @cfg {Boolean} striped striped of the progress bar
22044  * @cfg {Boolean} active animated of the progress bar
22045  * 
22046  * 
22047  * @constructor
22048  * Create a new Progress
22049  * @param {Object} config The config object
22050  */
22051
22052 Roo.bootstrap.Progress = function(config){
22053     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22054 };
22055
22056 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22057     
22058     striped : false,
22059     active: false,
22060     
22061     getAutoCreate : function(){
22062         var cfg = {
22063             tag: 'div',
22064             cls: 'progress'
22065         };
22066         
22067         
22068         if(this.striped){
22069             cfg.cls += ' progress-striped';
22070         }
22071       
22072         if(this.active){
22073             cfg.cls += ' active';
22074         }
22075         
22076         
22077         return cfg;
22078     }
22079    
22080 });
22081
22082  
22083
22084  /*
22085  * - LGPL
22086  *
22087  * ProgressBar
22088  * 
22089  */
22090
22091 /**
22092  * @class Roo.bootstrap.ProgressBar
22093  * @extends Roo.bootstrap.Component
22094  * Bootstrap ProgressBar class
22095  * @cfg {Number} aria_valuenow aria-value now
22096  * @cfg {Number} aria_valuemin aria-value min
22097  * @cfg {Number} aria_valuemax aria-value max
22098  * @cfg {String} label label for the progress bar
22099  * @cfg {String} panel (success | info | warning | danger )
22100  * @cfg {String} role role of the progress bar
22101  * @cfg {String} sr_only text
22102  * 
22103  * 
22104  * @constructor
22105  * Create a new ProgressBar
22106  * @param {Object} config The config object
22107  */
22108
22109 Roo.bootstrap.ProgressBar = function(config){
22110     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22111 };
22112
22113 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22114     
22115     aria_valuenow : 0,
22116     aria_valuemin : 0,
22117     aria_valuemax : 100,
22118     label : false,
22119     panel : false,
22120     role : false,
22121     sr_only: false,
22122     
22123     getAutoCreate : function()
22124     {
22125         
22126         var cfg = {
22127             tag: 'div',
22128             cls: 'progress-bar',
22129             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22130         };
22131         
22132         if(this.sr_only){
22133             cfg.cn = {
22134                 tag: 'span',
22135                 cls: 'sr-only',
22136                 html: this.sr_only
22137             }
22138         }
22139         
22140         if(this.role){
22141             cfg.role = this.role;
22142         }
22143         
22144         if(this.aria_valuenow){
22145             cfg['aria-valuenow'] = this.aria_valuenow;
22146         }
22147         
22148         if(this.aria_valuemin){
22149             cfg['aria-valuemin'] = this.aria_valuemin;
22150         }
22151         
22152         if(this.aria_valuemax){
22153             cfg['aria-valuemax'] = this.aria_valuemax;
22154         }
22155         
22156         if(this.label && !this.sr_only){
22157             cfg.html = this.label;
22158         }
22159         
22160         if(this.panel){
22161             cfg.cls += ' progress-bar-' + this.panel;
22162         }
22163         
22164         return cfg;
22165     },
22166     
22167     update : function(aria_valuenow)
22168     {
22169         this.aria_valuenow = aria_valuenow;
22170         
22171         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22172     }
22173    
22174 });
22175
22176  
22177
22178  /**
22179  * @class Roo.bootstrap.TabGroup
22180  * @extends Roo.bootstrap.Column
22181  * @children Roo.bootstrap.TabPanel
22182  * Bootstrap Column class
22183  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22184  * @cfg {Boolean} carousel true to make the group behave like a carousel
22185  * @cfg {Boolean} bullets show bullets for the panels
22186  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22187  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22188  * @cfg {Boolean} showarrow (true|false) show arrow default true
22189  * 
22190  * @constructor
22191  * Create a new TabGroup
22192  * @param {Object} config The config object
22193  */
22194
22195 Roo.bootstrap.TabGroup = function(config){
22196     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22197     if (!this.navId) {
22198         this.navId = Roo.id();
22199     }
22200     this.tabs = [];
22201     Roo.bootstrap.TabGroup.register(this);
22202     
22203 };
22204
22205 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22206     
22207     carousel : false,
22208     transition : false,
22209     bullets : 0,
22210     timer : 0,
22211     autoslide : false,
22212     slideFn : false,
22213     slideOnTouch : false,
22214     showarrow : true,
22215     
22216     getAutoCreate : function()
22217     {
22218         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22219         
22220         cfg.cls += ' tab-content';
22221         
22222         if (this.carousel) {
22223             cfg.cls += ' carousel slide';
22224             
22225             cfg.cn = [{
22226                cls : 'carousel-inner',
22227                cn : []
22228             }];
22229         
22230             if(this.bullets  && !Roo.isTouch){
22231                 
22232                 var bullets = {
22233                     cls : 'carousel-bullets',
22234                     cn : []
22235                 };
22236                
22237                 if(this.bullets_cls){
22238                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22239                 }
22240                 
22241                 bullets.cn.push({
22242                     cls : 'clear'
22243                 });
22244                 
22245                 cfg.cn[0].cn.push(bullets);
22246             }
22247             
22248             if(this.showarrow){
22249                 cfg.cn[0].cn.push({
22250                     tag : 'div',
22251                     class : 'carousel-arrow',
22252                     cn : [
22253                         {
22254                             tag : 'div',
22255                             class : 'carousel-prev',
22256                             cn : [
22257                                 {
22258                                     tag : 'i',
22259                                     class : 'fa fa-chevron-left'
22260                                 }
22261                             ]
22262                         },
22263                         {
22264                             tag : 'div',
22265                             class : 'carousel-next',
22266                             cn : [
22267                                 {
22268                                     tag : 'i',
22269                                     class : 'fa fa-chevron-right'
22270                                 }
22271                             ]
22272                         }
22273                     ]
22274                 });
22275             }
22276             
22277         }
22278         
22279         return cfg;
22280     },
22281     
22282     initEvents:  function()
22283     {
22284 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22285 //            this.el.on("touchstart", this.onTouchStart, this);
22286 //        }
22287         
22288         if(this.autoslide){
22289             var _this = this;
22290             
22291             this.slideFn = window.setInterval(function() {
22292                 _this.showPanelNext();
22293             }, this.timer);
22294         }
22295         
22296         if(this.showarrow){
22297             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22298             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22299         }
22300         
22301         
22302     },
22303     
22304 //    onTouchStart : function(e, el, o)
22305 //    {
22306 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22307 //            return;
22308 //        }
22309 //        
22310 //        this.showPanelNext();
22311 //    },
22312     
22313     
22314     getChildContainer : function()
22315     {
22316         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22317     },
22318     
22319     /**
22320     * register a Navigation item
22321     * @param {Roo.bootstrap.nav.Item} the navitem to add
22322     */
22323     register : function(item)
22324     {
22325         this.tabs.push( item);
22326         item.navId = this.navId; // not really needed..
22327         this.addBullet();
22328     
22329     },
22330     
22331     getActivePanel : function()
22332     {
22333         var r = false;
22334         Roo.each(this.tabs, function(t) {
22335             if (t.active) {
22336                 r = t;
22337                 return false;
22338             }
22339             return null;
22340         });
22341         return r;
22342         
22343     },
22344     getPanelByName : function(n)
22345     {
22346         var r = false;
22347         Roo.each(this.tabs, function(t) {
22348             if (t.tabId == n) {
22349                 r = t;
22350                 return false;
22351             }
22352             return null;
22353         });
22354         return r;
22355     },
22356     indexOfPanel : function(p)
22357     {
22358         var r = false;
22359         Roo.each(this.tabs, function(t,i) {
22360             if (t.tabId == p.tabId) {
22361                 r = i;
22362                 return false;
22363             }
22364             return null;
22365         });
22366         return r;
22367     },
22368     /**
22369      * show a specific panel
22370      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22371      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22372      */
22373     showPanel : function (pan)
22374     {
22375         if(this.transition || typeof(pan) == 'undefined'){
22376             Roo.log("waiting for the transitionend");
22377             return false;
22378         }
22379         
22380         if (typeof(pan) == 'number') {
22381             pan = this.tabs[pan];
22382         }
22383         
22384         if (typeof(pan) == 'string') {
22385             pan = this.getPanelByName(pan);
22386         }
22387         
22388         var cur = this.getActivePanel();
22389         
22390         if(!pan || !cur){
22391             Roo.log('pan or acitve pan is undefined');
22392             return false;
22393         }
22394         
22395         if (pan.tabId == this.getActivePanel().tabId) {
22396             return true;
22397         }
22398         
22399         if (false === cur.fireEvent('beforedeactivate')) {
22400             return false;
22401         }
22402         
22403         if(this.bullets > 0 && !Roo.isTouch){
22404             this.setActiveBullet(this.indexOfPanel(pan));
22405         }
22406         
22407         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22408             
22409             //class="carousel-item carousel-item-next carousel-item-left"
22410             
22411             this.transition = true;
22412             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22413             var lr = dir == 'next' ? 'left' : 'right';
22414             pan.el.addClass(dir); // or prev
22415             pan.el.addClass('carousel-item-' + dir); // or prev
22416             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22417             cur.el.addClass(lr); // or right
22418             pan.el.addClass(lr);
22419             cur.el.addClass('carousel-item-' +lr); // or right
22420             pan.el.addClass('carousel-item-' +lr);
22421             
22422             
22423             var _this = this;
22424             cur.el.on('transitionend', function() {
22425                 Roo.log("trans end?");
22426                 
22427                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22428                 pan.setActive(true);
22429                 
22430                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22431                 cur.setActive(false);
22432                 
22433                 _this.transition = false;
22434                 
22435             }, this, { single:  true } );
22436             
22437             return true;
22438         }
22439         
22440         cur.setActive(false);
22441         pan.setActive(true);
22442         
22443         return true;
22444         
22445     },
22446     showPanelNext : function()
22447     {
22448         var i = this.indexOfPanel(this.getActivePanel());
22449         
22450         if (i >= this.tabs.length - 1 && !this.autoslide) {
22451             return;
22452         }
22453         
22454         if (i >= this.tabs.length - 1 && this.autoslide) {
22455             i = -1;
22456         }
22457         
22458         this.showPanel(this.tabs[i+1]);
22459     },
22460     
22461     showPanelPrev : function()
22462     {
22463         var i = this.indexOfPanel(this.getActivePanel());
22464         
22465         if (i  < 1 && !this.autoslide) {
22466             return;
22467         }
22468         
22469         if (i < 1 && this.autoslide) {
22470             i = this.tabs.length;
22471         }
22472         
22473         this.showPanel(this.tabs[i-1]);
22474     },
22475     
22476     
22477     addBullet: function()
22478     {
22479         if(!this.bullets || Roo.isTouch){
22480             return;
22481         }
22482         var ctr = this.el.select('.carousel-bullets',true).first();
22483         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22484         var bullet = ctr.createChild({
22485             cls : 'bullet bullet-' + i
22486         },ctr.dom.lastChild);
22487         
22488         
22489         var _this = this;
22490         
22491         bullet.on('click', (function(e, el, o, ii, t){
22492
22493             e.preventDefault();
22494
22495             this.showPanel(ii);
22496
22497             if(this.autoslide && this.slideFn){
22498                 clearInterval(this.slideFn);
22499                 this.slideFn = window.setInterval(function() {
22500                     _this.showPanelNext();
22501                 }, this.timer);
22502             }
22503
22504         }).createDelegate(this, [i, bullet], true));
22505                 
22506         
22507     },
22508      
22509     setActiveBullet : function(i)
22510     {
22511         if(Roo.isTouch){
22512             return;
22513         }
22514         
22515         Roo.each(this.el.select('.bullet', true).elements, function(el){
22516             el.removeClass('selected');
22517         });
22518
22519         var bullet = this.el.select('.bullet-' + i, true).first();
22520         
22521         if(!bullet){
22522             return;
22523         }
22524         
22525         bullet.addClass('selected');
22526     }
22527     
22528     
22529   
22530 });
22531
22532  
22533
22534  
22535  
22536 Roo.apply(Roo.bootstrap.TabGroup, {
22537     
22538     groups: {},
22539      /**
22540     * register a Navigation Group
22541     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22542     */
22543     register : function(navgrp)
22544     {
22545         this.groups[navgrp.navId] = navgrp;
22546         
22547     },
22548     /**
22549     * fetch a Navigation Group based on the navigation ID
22550     * if one does not exist , it will get created.
22551     * @param {string} the navgroup to add
22552     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22553     */
22554     get: function(navId) {
22555         if (typeof(this.groups[navId]) == 'undefined') {
22556             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22557         }
22558         return this.groups[navId] ;
22559     }
22560     
22561     
22562     
22563 });
22564
22565  /*
22566  * - LGPL
22567  *
22568  * TabPanel
22569  * 
22570  */
22571
22572 /**
22573  * @class Roo.bootstrap.TabPanel
22574  * @extends Roo.bootstrap.Component
22575  * @children Roo.bootstrap.Component
22576  * Bootstrap TabPanel class
22577  * @cfg {Boolean} active panel active
22578  * @cfg {String} html panel content
22579  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22580  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22581  * @cfg {String} href click to link..
22582  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22583  * 
22584  * 
22585  * @constructor
22586  * Create a new TabPanel
22587  * @param {Object} config The config object
22588  */
22589
22590 Roo.bootstrap.TabPanel = function(config){
22591     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22592     this.addEvents({
22593         /**
22594              * @event changed
22595              * Fires when the active status changes
22596              * @param {Roo.bootstrap.TabPanel} this
22597              * @param {Boolean} state the new state
22598             
22599          */
22600         'changed': true,
22601         /**
22602              * @event beforedeactivate
22603              * Fires before a tab is de-activated - can be used to do validation on a form.
22604              * @param {Roo.bootstrap.TabPanel} this
22605              * @return {Boolean} false if there is an error
22606             
22607          */
22608         'beforedeactivate': true
22609      });
22610     
22611     this.tabId = this.tabId || Roo.id();
22612   
22613 };
22614
22615 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22616     
22617     active: false,
22618     html: false,
22619     tabId: false,
22620     navId : false,
22621     href : '',
22622     touchSlide : false,
22623     getAutoCreate : function(){
22624         
22625         
22626         var cfg = {
22627             tag: 'div',
22628             // item is needed for carousel - not sure if it has any effect otherwise
22629             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22630             html: this.html || ''
22631         };
22632         
22633         if(this.active){
22634             cfg.cls += ' active';
22635         }
22636         
22637         if(this.tabId){
22638             cfg.tabId = this.tabId;
22639         }
22640         
22641         
22642         
22643         return cfg;
22644     },
22645     
22646     initEvents:  function()
22647     {
22648         var p = this.parent();
22649         
22650         this.navId = this.navId || p.navId;
22651         
22652         if (typeof(this.navId) != 'undefined') {
22653             // not really needed.. but just in case.. parent should be a NavGroup.
22654             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22655             
22656             tg.register(this);
22657             
22658             var i = tg.tabs.length - 1;
22659             
22660             if(this.active && tg.bullets > 0 && i < tg.bullets){
22661                 tg.setActiveBullet(i);
22662             }
22663         }
22664         
22665         this.el.on('click', this.onClick, this);
22666         
22667         if(Roo.isTouch && this.touchSlide){
22668             this.el.on("touchstart", this.onTouchStart, this);
22669             this.el.on("touchmove", this.onTouchMove, this);
22670             this.el.on("touchend", this.onTouchEnd, this);
22671         }
22672         
22673     },
22674     
22675     onRender : function(ct, position)
22676     {
22677         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22678     },
22679     
22680     setActive : function(state)
22681     {
22682         Roo.log("panel - set active " + this.tabId + "=" + state);
22683         
22684         this.active = state;
22685         if (!state) {
22686             this.el.removeClass('active');
22687             
22688         } else  if (!this.el.hasClass('active')) {
22689             this.el.addClass('active');
22690         }
22691         
22692         this.fireEvent('changed', this, state);
22693     },
22694     
22695     onClick : function(e)
22696     {
22697         e.preventDefault();
22698         
22699         if(!this.href.length){
22700             return;
22701         }
22702         
22703         window.location.href = this.href;
22704     },
22705     
22706     startX : 0,
22707     startY : 0,
22708     endX : 0,
22709     endY : 0,
22710     swiping : false,
22711     
22712     onTouchStart : function(e)
22713     {
22714         this.swiping = false;
22715         
22716         this.startX = e.browserEvent.touches[0].clientX;
22717         this.startY = e.browserEvent.touches[0].clientY;
22718     },
22719     
22720     onTouchMove : function(e)
22721     {
22722         this.swiping = true;
22723         
22724         this.endX = e.browserEvent.touches[0].clientX;
22725         this.endY = e.browserEvent.touches[0].clientY;
22726     },
22727     
22728     onTouchEnd : function(e)
22729     {
22730         if(!this.swiping){
22731             this.onClick(e);
22732             return;
22733         }
22734         
22735         var tabGroup = this.parent();
22736         
22737         if(this.endX > this.startX){ // swiping right
22738             tabGroup.showPanelPrev();
22739             return;
22740         }
22741         
22742         if(this.startX > this.endX){ // swiping left
22743             tabGroup.showPanelNext();
22744             return;
22745         }
22746     }
22747     
22748     
22749 });
22750  
22751
22752  
22753
22754  /*
22755  * - LGPL
22756  *
22757  * DateField
22758  * 
22759  */
22760
22761 /**
22762  * @class Roo.bootstrap.form.DateField
22763  * @extends Roo.bootstrap.form.Input
22764  * Bootstrap DateField class
22765  * @cfg {Number} weekStart default 0
22766  * @cfg {String} viewMode default empty, (months|years)
22767  * @cfg {String} minViewMode default empty, (months|years)
22768  * @cfg {Number} startDate default -Infinity
22769  * @cfg {Number} endDate default Infinity
22770  * @cfg {Boolean} todayHighlight default false
22771  * @cfg {Boolean} todayBtn default false
22772  * @cfg {Boolean} calendarWeeks default false
22773  * @cfg {Object} daysOfWeekDisabled default empty
22774  * @cfg {Boolean} singleMode default false (true | false)
22775  * 
22776  * @cfg {Boolean} keyboardNavigation default true
22777  * @cfg {String} language default en
22778  * 
22779  * @constructor
22780  * Create a new DateField
22781  * @param {Object} config The config object
22782  */
22783
22784 Roo.bootstrap.form.DateField = function(config){
22785     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22786      this.addEvents({
22787             /**
22788              * @event show
22789              * Fires when this field show.
22790              * @param {Roo.bootstrap.form.DateField} this
22791              * @param {Mixed} date The date value
22792              */
22793             show : true,
22794             /**
22795              * @event show
22796              * Fires when this field hide.
22797              * @param {Roo.bootstrap.form.DateField} this
22798              * @param {Mixed} date The date value
22799              */
22800             hide : true,
22801             /**
22802              * @event select
22803              * Fires when select a date.
22804              * @param {Roo.bootstrap.form.DateField} this
22805              * @param {Mixed} date The date value
22806              */
22807             select : true,
22808             /**
22809              * @event beforeselect
22810              * Fires when before select a date.
22811              * @param {Roo.bootstrap.form.DateField} this
22812              * @param {Mixed} date The date value
22813              */
22814             beforeselect : true
22815         });
22816 };
22817
22818 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22819     
22820     /**
22821      * @cfg {String} format
22822      * The default date format string which can be overriden for localization support.  The format must be
22823      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22824      */
22825     format : "m/d/y",
22826     /**
22827      * @cfg {String} altFormats
22828      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22829      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22830      */
22831     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22832     
22833     weekStart : 0,
22834     
22835     viewMode : '',
22836     
22837     minViewMode : '',
22838     
22839     todayHighlight : false,
22840     
22841     todayBtn: false,
22842     
22843     language: 'en',
22844     
22845     keyboardNavigation: true,
22846     
22847     calendarWeeks: false,
22848     
22849     startDate: -Infinity,
22850     
22851     endDate: Infinity,
22852     
22853     daysOfWeekDisabled: [],
22854     
22855     _events: [],
22856     
22857     singleMode : false,
22858     
22859     UTCDate: function()
22860     {
22861         return new Date(Date.UTC.apply(Date, arguments));
22862     },
22863     
22864     UTCToday: function()
22865     {
22866         var today = new Date();
22867         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22868     },
22869     
22870     getDate: function() {
22871             var d = this.getUTCDate();
22872             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22873     },
22874     
22875     getUTCDate: function() {
22876             return this.date;
22877     },
22878     
22879     setDate: function(d) {
22880             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22881     },
22882     
22883     setUTCDate: function(d) {
22884             this.date = d;
22885             this.setValue(this.formatDate(this.date));
22886     },
22887         
22888     onRender: function(ct, position)
22889     {
22890         
22891         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22892         
22893         this.language = this.language || 'en';
22894         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22895         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22896         
22897         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22898         this.format = this.format || 'm/d/y';
22899         this.isInline = false;
22900         this.isInput = true;
22901         this.component = this.el.select('.add-on', true).first() || false;
22902         this.component = (this.component && this.component.length === 0) ? false : this.component;
22903         this.hasInput = this.component && this.inputEl().length;
22904         
22905         if (typeof(this.minViewMode === 'string')) {
22906             switch (this.minViewMode) {
22907                 case 'months':
22908                     this.minViewMode = 1;
22909                     break;
22910                 case 'years':
22911                     this.minViewMode = 2;
22912                     break;
22913                 default:
22914                     this.minViewMode = 0;
22915                     break;
22916             }
22917         }
22918         
22919         if (typeof(this.viewMode === 'string')) {
22920             switch (this.viewMode) {
22921                 case 'months':
22922                     this.viewMode = 1;
22923                     break;
22924                 case 'years':
22925                     this.viewMode = 2;
22926                     break;
22927                 default:
22928                     this.viewMode = 0;
22929                     break;
22930             }
22931         }
22932                 
22933         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22934         
22935 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22936         
22937         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22938         
22939         this.picker().on('mousedown', this.onMousedown, this);
22940         this.picker().on('click', this.onClick, this);
22941         
22942         this.picker().addClass('datepicker-dropdown');
22943         
22944         this.startViewMode = this.viewMode;
22945         
22946         if(this.singleMode){
22947             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22948                 v.setVisibilityMode(Roo.Element.DISPLAY);
22949                 v.hide();
22950             });
22951             
22952             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22953                 v.setStyle('width', '189px');
22954             });
22955         }
22956         
22957         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22958             if(!this.calendarWeeks){
22959                 v.remove();
22960                 return;
22961             }
22962             
22963             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22964             v.attr('colspan', function(i, val){
22965                 return parseInt(val) + 1;
22966             });
22967         });
22968                         
22969         
22970         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22971         
22972         this.setStartDate(this.startDate);
22973         this.setEndDate(this.endDate);
22974         
22975         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22976         
22977         this.fillDow();
22978         this.fillMonths();
22979         this.update();
22980         this.showMode();
22981         
22982         if(this.isInline) {
22983             this.showPopup();
22984         }
22985     },
22986     
22987     picker : function()
22988     {
22989         return this.pickerEl;
22990 //        return this.el.select('.datepicker', true).first();
22991     },
22992     
22993     fillDow: function()
22994     {
22995         var dowCnt = this.weekStart;
22996         
22997         var dow = {
22998             tag: 'tr',
22999             cn: [
23000                 
23001             ]
23002         };
23003         
23004         if(this.calendarWeeks){
23005             dow.cn.push({
23006                 tag: 'th',
23007                 cls: 'cw',
23008                 html: '&nbsp;'
23009             })
23010         }
23011         
23012         while (dowCnt < this.weekStart + 7) {
23013             dow.cn.push({
23014                 tag: 'th',
23015                 cls: 'dow',
23016                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23017             });
23018         }
23019         
23020         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23021     },
23022     
23023     fillMonths: function()
23024     {    
23025         var i = 0;
23026         var months = this.picker().select('>.datepicker-months td', true).first();
23027         
23028         months.dom.innerHTML = '';
23029         
23030         while (i < 12) {
23031             var month = {
23032                 tag: 'span',
23033                 cls: 'month',
23034                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23035             };
23036             
23037             months.createChild(month);
23038         }
23039         
23040     },
23041     
23042     update: function()
23043     {
23044         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;
23045         
23046         if (this.date < this.startDate) {
23047             this.viewDate = new Date(this.startDate);
23048         } else if (this.date > this.endDate) {
23049             this.viewDate = new Date(this.endDate);
23050         } else {
23051             this.viewDate = new Date(this.date);
23052         }
23053         
23054         this.fill();
23055     },
23056     
23057     fill: function() 
23058     {
23059         var d = new Date(this.viewDate),
23060                 year = d.getUTCFullYear(),
23061                 month = d.getUTCMonth(),
23062                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23063                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23064                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23065                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23066                 currentDate = this.date && this.date.valueOf(),
23067                 today = this.UTCToday();
23068         
23069         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23070         
23071 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23072         
23073 //        this.picker.select('>tfoot th.today').
23074 //                                              .text(dates[this.language].today)
23075 //                                              .toggle(this.todayBtn !== false);
23076     
23077         this.updateNavArrows();
23078         this.fillMonths();
23079                                                 
23080         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23081         
23082         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23083          
23084         prevMonth.setUTCDate(day);
23085         
23086         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23087         
23088         var nextMonth = new Date(prevMonth);
23089         
23090         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23091         
23092         nextMonth = nextMonth.valueOf();
23093         
23094         var fillMonths = false;
23095         
23096         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23097         
23098         while(prevMonth.valueOf() <= nextMonth) {
23099             var clsName = '';
23100             
23101             if (prevMonth.getUTCDay() === this.weekStart) {
23102                 if(fillMonths){
23103                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23104                 }
23105                     
23106                 fillMonths = {
23107                     tag: 'tr',
23108                     cn: []
23109                 };
23110                 
23111                 if(this.calendarWeeks){
23112                     // ISO 8601: First week contains first thursday.
23113                     // ISO also states week starts on Monday, but we can be more abstract here.
23114                     var
23115                     // Start of current week: based on weekstart/current date
23116                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23117                     // Thursday of this week
23118                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23119                     // First Thursday of year, year from thursday
23120                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23121                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23122                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23123                     
23124                     fillMonths.cn.push({
23125                         tag: 'td',
23126                         cls: 'cw',
23127                         html: calWeek
23128                     });
23129                 }
23130             }
23131             
23132             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23133                 clsName += ' old';
23134             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23135                 clsName += ' new';
23136             }
23137             if (this.todayHighlight &&
23138                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23139                 prevMonth.getUTCMonth() == today.getMonth() &&
23140                 prevMonth.getUTCDate() == today.getDate()) {
23141                 clsName += ' today';
23142             }
23143             
23144             if (currentDate && prevMonth.valueOf() === currentDate) {
23145                 clsName += ' active';
23146             }
23147             
23148             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23149                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23150                     clsName += ' disabled';
23151             }
23152             
23153             fillMonths.cn.push({
23154                 tag: 'td',
23155                 cls: 'day ' + clsName,
23156                 html: prevMonth.getDate()
23157             });
23158             
23159             prevMonth.setDate(prevMonth.getDate()+1);
23160         }
23161           
23162         var currentYear = this.date && this.date.getUTCFullYear();
23163         var currentMonth = this.date && this.date.getUTCMonth();
23164         
23165         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23166         
23167         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23168             v.removeClass('active');
23169             
23170             if(currentYear === year && k === currentMonth){
23171                 v.addClass('active');
23172             }
23173             
23174             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23175                 v.addClass('disabled');
23176             }
23177             
23178         });
23179         
23180         
23181         year = parseInt(year/10, 10) * 10;
23182         
23183         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23184         
23185         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23186         
23187         year -= 1;
23188         for (var i = -1; i < 11; i++) {
23189             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23190                 tag: 'span',
23191                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23192                 html: year
23193             });
23194             
23195             year += 1;
23196         }
23197     },
23198     
23199     showMode: function(dir) 
23200     {
23201         if (dir) {
23202             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23203         }
23204         
23205         Roo.each(this.picker().select('>div',true).elements, function(v){
23206             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23207             v.hide();
23208         });
23209         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23210     },
23211     
23212     place: function()
23213     {
23214         if(this.isInline) {
23215             return;
23216         }
23217         
23218         this.picker().removeClass(['bottom', 'top']);
23219         
23220         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23221             /*
23222              * place to the top of element!
23223              *
23224              */
23225             
23226             this.picker().addClass('top');
23227             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23228             
23229             return;
23230         }
23231         
23232         this.picker().addClass('bottom');
23233         
23234         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23235     },
23236     
23237     parseDate : function(value)
23238     {
23239         if(!value || value instanceof Date){
23240             return value;
23241         }
23242         var v = Date.parseDate(value, this.format);
23243         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23244             v = Date.parseDate(value, 'Y-m-d');
23245         }
23246         if(!v && this.altFormats){
23247             if(!this.altFormatsArray){
23248                 this.altFormatsArray = this.altFormats.split("|");
23249             }
23250             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23251                 v = Date.parseDate(value, this.altFormatsArray[i]);
23252             }
23253         }
23254         return v;
23255     },
23256     
23257     formatDate : function(date, fmt)
23258     {   
23259         return (!date || !(date instanceof Date)) ?
23260         date : date.dateFormat(fmt || this.format);
23261     },
23262     
23263     onFocus : function()
23264     {
23265         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23266         this.showPopup();
23267     },
23268     
23269     onBlur : function()
23270     {
23271         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23272         
23273         var d = this.inputEl().getValue();
23274         
23275         this.setValue(d);
23276                 
23277         this.hidePopup();
23278     },
23279     
23280     showPopup : function()
23281     {
23282         this.picker().show();
23283         this.update();
23284         this.place();
23285         
23286         this.fireEvent('showpopup', this, this.date);
23287     },
23288     
23289     hidePopup : function()
23290     {
23291         if(this.isInline) {
23292             return;
23293         }
23294         this.picker().hide();
23295         this.viewMode = this.startViewMode;
23296         this.showMode();
23297         
23298         this.fireEvent('hidepopup', this, this.date);
23299         
23300     },
23301     
23302     onMousedown: function(e)
23303     {
23304         e.stopPropagation();
23305         e.preventDefault();
23306     },
23307     
23308     keyup: function(e)
23309     {
23310         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23311         this.update();
23312     },
23313
23314     setValue: function(v)
23315     {
23316         if(this.fireEvent('beforeselect', this, v) !== false){
23317             var d = new Date(this.parseDate(v) ).clearTime();
23318         
23319             if(isNaN(d.getTime())){
23320                 this.date = this.viewDate = '';
23321                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23322                 return;
23323             }
23324
23325             v = this.formatDate(d);
23326
23327             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23328
23329             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23330
23331             this.update();
23332
23333             this.fireEvent('select', this, this.date);
23334         }
23335     },
23336     
23337     getValue: function()
23338     {
23339         return this.formatDate(this.date);
23340     },
23341     
23342     fireKey: function(e)
23343     {
23344         if (!this.picker().isVisible()){
23345             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23346                 this.showPopup();
23347             }
23348             return;
23349         }
23350         
23351         var dateChanged = false,
23352         dir, day, month,
23353         newDate, newViewDate;
23354         
23355         switch(e.keyCode){
23356             case 27: // escape
23357                 this.hidePopup();
23358                 e.preventDefault();
23359                 break;
23360             case 37: // left
23361             case 39: // right
23362                 if (!this.keyboardNavigation) {
23363                     break;
23364                 }
23365                 dir = e.keyCode == 37 ? -1 : 1;
23366                 
23367                 if (e.ctrlKey){
23368                     newDate = this.moveYear(this.date, dir);
23369                     newViewDate = this.moveYear(this.viewDate, dir);
23370                 } else if (e.shiftKey){
23371                     newDate = this.moveMonth(this.date, dir);
23372                     newViewDate = this.moveMonth(this.viewDate, dir);
23373                 } else {
23374                     newDate = new Date(this.date);
23375                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23376                     newViewDate = new Date(this.viewDate);
23377                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23378                 }
23379                 if (this.dateWithinRange(newDate)){
23380                     this.date = newDate;
23381                     this.viewDate = newViewDate;
23382                     this.setValue(this.formatDate(this.date));
23383 //                    this.update();
23384                     e.preventDefault();
23385                     dateChanged = true;
23386                 }
23387                 break;
23388             case 38: // up
23389             case 40: // down
23390                 if (!this.keyboardNavigation) {
23391                     break;
23392                 }
23393                 dir = e.keyCode == 38 ? -1 : 1;
23394                 if (e.ctrlKey){
23395                     newDate = this.moveYear(this.date, dir);
23396                     newViewDate = this.moveYear(this.viewDate, dir);
23397                 } else if (e.shiftKey){
23398                     newDate = this.moveMonth(this.date, dir);
23399                     newViewDate = this.moveMonth(this.viewDate, dir);
23400                 } else {
23401                     newDate = new Date(this.date);
23402                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23403                     newViewDate = new Date(this.viewDate);
23404                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23405                 }
23406                 if (this.dateWithinRange(newDate)){
23407                     this.date = newDate;
23408                     this.viewDate = newViewDate;
23409                     this.setValue(this.formatDate(this.date));
23410 //                    this.update();
23411                     e.preventDefault();
23412                     dateChanged = true;
23413                 }
23414                 break;
23415             case 13: // enter
23416                 this.setValue(this.formatDate(this.date));
23417                 this.hidePopup();
23418                 e.preventDefault();
23419                 break;
23420             case 9: // tab
23421                 this.setValue(this.formatDate(this.date));
23422                 this.hidePopup();
23423                 break;
23424             case 16: // shift
23425             case 17: // ctrl
23426             case 18: // alt
23427                 break;
23428             default :
23429                 this.hidePopup();
23430                 
23431         }
23432     },
23433     
23434     
23435     onClick: function(e) 
23436     {
23437         e.stopPropagation();
23438         e.preventDefault();
23439         
23440         var target = e.getTarget();
23441         
23442         if(target.nodeName.toLowerCase() === 'i'){
23443             target = Roo.get(target).dom.parentNode;
23444         }
23445         
23446         var nodeName = target.nodeName;
23447         var className = target.className;
23448         var html = target.innerHTML;
23449         //Roo.log(nodeName);
23450         
23451         switch(nodeName.toLowerCase()) {
23452             case 'th':
23453                 switch(className) {
23454                     case 'switch':
23455                         this.showMode(1);
23456                         break;
23457                     case 'prev':
23458                     case 'next':
23459                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23460                         switch(this.viewMode){
23461                                 case 0:
23462                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23463                                         break;
23464                                 case 1:
23465                                 case 2:
23466                                         this.viewDate = this.moveYear(this.viewDate, dir);
23467                                         break;
23468                         }
23469                         this.fill();
23470                         break;
23471                     case 'today':
23472                         var date = new Date();
23473                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23474 //                        this.fill()
23475                         this.setValue(this.formatDate(this.date));
23476                         
23477                         this.hidePopup();
23478                         break;
23479                 }
23480                 break;
23481             case 'span':
23482                 if (className.indexOf('disabled') < 0) {
23483                 if (!this.viewDate) {
23484                     this.viewDate = new Date();
23485                 }
23486                 this.viewDate.setUTCDate(1);
23487                     if (className.indexOf('month') > -1) {
23488                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23489                     } else {
23490                         var year = parseInt(html, 10) || 0;
23491                         this.viewDate.setUTCFullYear(year);
23492                         
23493                     }
23494                     
23495                     if(this.singleMode){
23496                         this.setValue(this.formatDate(this.viewDate));
23497                         this.hidePopup();
23498                         return;
23499                     }
23500                     
23501                     this.showMode(-1);
23502                     this.fill();
23503                 }
23504                 break;
23505                 
23506             case 'td':
23507                 //Roo.log(className);
23508                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23509                     var day = parseInt(html, 10) || 1;
23510                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23511                         month = (this.viewDate || new Date()).getUTCMonth();
23512
23513                     if (className.indexOf('old') > -1) {
23514                         if(month === 0 ){
23515                             month = 11;
23516                             year -= 1;
23517                         }else{
23518                             month -= 1;
23519                         }
23520                     } else if (className.indexOf('new') > -1) {
23521                         if (month == 11) {
23522                             month = 0;
23523                             year += 1;
23524                         } else {
23525                             month += 1;
23526                         }
23527                     }
23528                     //Roo.log([year,month,day]);
23529                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23530                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23531 //                    this.fill();
23532                     //Roo.log(this.formatDate(this.date));
23533                     this.setValue(this.formatDate(this.date));
23534                     this.hidePopup();
23535                 }
23536                 break;
23537         }
23538     },
23539     
23540     setStartDate: function(startDate)
23541     {
23542         this.startDate = startDate || -Infinity;
23543         if (this.startDate !== -Infinity) {
23544             this.startDate = this.parseDate(this.startDate);
23545         }
23546         this.update();
23547         this.updateNavArrows();
23548     },
23549
23550     setEndDate: function(endDate)
23551     {
23552         this.endDate = endDate || Infinity;
23553         if (this.endDate !== Infinity) {
23554             this.endDate = this.parseDate(this.endDate);
23555         }
23556         this.update();
23557         this.updateNavArrows();
23558     },
23559     
23560     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23561     {
23562         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23563         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23564             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23565         }
23566         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23567             return parseInt(d, 10);
23568         });
23569         this.update();
23570         this.updateNavArrows();
23571     },
23572     
23573     updateNavArrows: function() 
23574     {
23575         if(this.singleMode){
23576             return;
23577         }
23578         
23579         var d = new Date(this.viewDate),
23580         year = d.getUTCFullYear(),
23581         month = d.getUTCMonth();
23582         
23583         Roo.each(this.picker().select('.prev', true).elements, function(v){
23584             v.show();
23585             switch (this.viewMode) {
23586                 case 0:
23587
23588                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23589                         v.hide();
23590                     }
23591                     break;
23592                 case 1:
23593                 case 2:
23594                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23595                         v.hide();
23596                     }
23597                     break;
23598             }
23599         });
23600         
23601         Roo.each(this.picker().select('.next', true).elements, function(v){
23602             v.show();
23603             switch (this.viewMode) {
23604                 case 0:
23605
23606                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23607                         v.hide();
23608                     }
23609                     break;
23610                 case 1:
23611                 case 2:
23612                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23613                         v.hide();
23614                     }
23615                     break;
23616             }
23617         })
23618     },
23619     
23620     moveMonth: function(date, dir)
23621     {
23622         if (!dir) {
23623             return date;
23624         }
23625         var new_date = new Date(date.valueOf()),
23626         day = new_date.getUTCDate(),
23627         month = new_date.getUTCMonth(),
23628         mag = Math.abs(dir),
23629         new_month, test;
23630         dir = dir > 0 ? 1 : -1;
23631         if (mag == 1){
23632             test = dir == -1
23633             // If going back one month, make sure month is not current month
23634             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23635             ? function(){
23636                 return new_date.getUTCMonth() == month;
23637             }
23638             // If going forward one month, make sure month is as expected
23639             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23640             : function(){
23641                 return new_date.getUTCMonth() != new_month;
23642             };
23643             new_month = month + dir;
23644             new_date.setUTCMonth(new_month);
23645             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23646             if (new_month < 0 || new_month > 11) {
23647                 new_month = (new_month + 12) % 12;
23648             }
23649         } else {
23650             // For magnitudes >1, move one month at a time...
23651             for (var i=0; i<mag; i++) {
23652                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23653                 new_date = this.moveMonth(new_date, dir);
23654             }
23655             // ...then reset the day, keeping it in the new month
23656             new_month = new_date.getUTCMonth();
23657             new_date.setUTCDate(day);
23658             test = function(){
23659                 return new_month != new_date.getUTCMonth();
23660             };
23661         }
23662         // Common date-resetting loop -- if date is beyond end of month, make it
23663         // end of month
23664         while (test()){
23665             new_date.setUTCDate(--day);
23666             new_date.setUTCMonth(new_month);
23667         }
23668         return new_date;
23669     },
23670
23671     moveYear: function(date, dir)
23672     {
23673         return this.moveMonth(date, dir*12);
23674     },
23675
23676     dateWithinRange: function(date)
23677     {
23678         return date >= this.startDate && date <= this.endDate;
23679     },
23680
23681     
23682     remove: function() 
23683     {
23684         this.picker().remove();
23685     },
23686     
23687     validateValue : function(value)
23688     {
23689         if(this.getVisibilityEl().hasClass('hidden')){
23690             return true;
23691         }
23692         
23693         if(value.length < 1)  {
23694             if(this.allowBlank){
23695                 return true;
23696             }
23697             return false;
23698         }
23699         
23700         if(value.length < this.minLength){
23701             return false;
23702         }
23703         if(value.length > this.maxLength){
23704             return false;
23705         }
23706         if(this.vtype){
23707             var vt = Roo.form.VTypes;
23708             if(!vt[this.vtype](value, this)){
23709                 return false;
23710             }
23711         }
23712         if(typeof this.validator == "function"){
23713             var msg = this.validator(value);
23714             if(msg !== true){
23715                 return false;
23716             }
23717         }
23718         
23719         if(this.regex && !this.regex.test(value)){
23720             return false;
23721         }
23722         
23723         if(typeof(this.parseDate(value)) == 'undefined'){
23724             return false;
23725         }
23726         
23727         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23728             return false;
23729         }      
23730         
23731         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23732             return false;
23733         } 
23734         
23735         
23736         return true;
23737     },
23738     
23739     reset : function()
23740     {
23741         this.date = this.viewDate = '';
23742         
23743         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23744     }
23745    
23746 });
23747
23748 Roo.apply(Roo.bootstrap.form.DateField,  {
23749     
23750     head : {
23751         tag: 'thead',
23752         cn: [
23753         {
23754             tag: 'tr',
23755             cn: [
23756             {
23757                 tag: 'th',
23758                 cls: 'prev',
23759                 html: '<i class="fa fa-arrow-left"/>'
23760             },
23761             {
23762                 tag: 'th',
23763                 cls: 'switch',
23764                 colspan: '5'
23765             },
23766             {
23767                 tag: 'th',
23768                 cls: 'next',
23769                 html: '<i class="fa fa-arrow-right"/>'
23770             }
23771
23772             ]
23773         }
23774         ]
23775     },
23776     
23777     content : {
23778         tag: 'tbody',
23779         cn: [
23780         {
23781             tag: 'tr',
23782             cn: [
23783             {
23784                 tag: 'td',
23785                 colspan: '7'
23786             }
23787             ]
23788         }
23789         ]
23790     },
23791     
23792     footer : {
23793         tag: 'tfoot',
23794         cn: [
23795         {
23796             tag: 'tr',
23797             cn: [
23798             {
23799                 tag: 'th',
23800                 colspan: '7',
23801                 cls: 'today'
23802             }
23803                     
23804             ]
23805         }
23806         ]
23807     },
23808     
23809     dates:{
23810         en: {
23811             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23812             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23813             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23814             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23815             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23816             today: "Today"
23817         }
23818     },
23819     
23820     modes: [
23821     {
23822         clsName: 'days',
23823         navFnc: 'Month',
23824         navStep: 1
23825     },
23826     {
23827         clsName: 'months',
23828         navFnc: 'FullYear',
23829         navStep: 1
23830     },
23831     {
23832         clsName: 'years',
23833         navFnc: 'FullYear',
23834         navStep: 10
23835     }]
23836 });
23837
23838 Roo.apply(Roo.bootstrap.form.DateField,  {
23839   
23840     template : {
23841         tag: 'div',
23842         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23843         cn: [
23844         {
23845             tag: 'div',
23846             cls: 'datepicker-days',
23847             cn: [
23848             {
23849                 tag: 'table',
23850                 cls: 'table-condensed',
23851                 cn:[
23852                 Roo.bootstrap.form.DateField.head,
23853                 {
23854                     tag: 'tbody'
23855                 },
23856                 Roo.bootstrap.form.DateField.footer
23857                 ]
23858             }
23859             ]
23860         },
23861         {
23862             tag: 'div',
23863             cls: 'datepicker-months',
23864             cn: [
23865             {
23866                 tag: 'table',
23867                 cls: 'table-condensed',
23868                 cn:[
23869                 Roo.bootstrap.form.DateField.head,
23870                 Roo.bootstrap.form.DateField.content,
23871                 Roo.bootstrap.form.DateField.footer
23872                 ]
23873             }
23874             ]
23875         },
23876         {
23877             tag: 'div',
23878             cls: 'datepicker-years',
23879             cn: [
23880             {
23881                 tag: 'table',
23882                 cls: 'table-condensed',
23883                 cn:[
23884                 Roo.bootstrap.form.DateField.head,
23885                 Roo.bootstrap.form.DateField.content,
23886                 Roo.bootstrap.form.DateField.footer
23887                 ]
23888             }
23889             ]
23890         }
23891         ]
23892     }
23893 });
23894
23895  
23896
23897  /*
23898  * - LGPL
23899  *
23900  * TimeField
23901  * 
23902  */
23903
23904 /**
23905  * @class Roo.bootstrap.form.TimeField
23906  * @extends Roo.bootstrap.form.Input
23907  * Bootstrap DateField class
23908  * 
23909  * 
23910  * @constructor
23911  * Create a new TimeField
23912  * @param {Object} config The config object
23913  */
23914
23915 Roo.bootstrap.form.TimeField = function(config){
23916     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23917     this.addEvents({
23918             /**
23919              * @event show
23920              * Fires when this field show.
23921              * @param {Roo.bootstrap.form.DateField} thisthis
23922              * @param {Mixed} date The date value
23923              */
23924             show : true,
23925             /**
23926              * @event show
23927              * Fires when this field hide.
23928              * @param {Roo.bootstrap.form.DateField} this
23929              * @param {Mixed} date The date value
23930              */
23931             hide : true,
23932             /**
23933              * @event select
23934              * Fires when select a date.
23935              * @param {Roo.bootstrap.form.DateField} this
23936              * @param {Mixed} date The date value
23937              */
23938             select : true
23939         });
23940 };
23941
23942 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23943     
23944     /**
23945      * @cfg {String} format
23946      * The default time format string which can be overriden for localization support.  The format must be
23947      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23948      */
23949     format : "H:i",
23950
23951     getAutoCreate : function()
23952     {
23953         this.after = '<i class="fa far fa-clock"></i>';
23954         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23955         
23956          
23957     },
23958     onRender: function(ct, position)
23959     {
23960         
23961         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23962                 
23963         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23964         
23965         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23966         
23967         this.pop = this.picker().select('>.datepicker-time',true).first();
23968         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23969         
23970         this.picker().on('mousedown', this.onMousedown, this);
23971         this.picker().on('click', this.onClick, this);
23972         
23973         this.picker().addClass('datepicker-dropdown');
23974     
23975         this.fillTime();
23976         this.update();
23977             
23978         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23979         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23980         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23981         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23982         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23983         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23984
23985     },
23986     
23987     fireKey: function(e){
23988         if (!this.picker().isVisible()){
23989             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23990                 this.show();
23991             }
23992             return;
23993         }
23994
23995         e.preventDefault();
23996         
23997         switch(e.keyCode){
23998             case 27: // escape
23999                 this.hide();
24000                 break;
24001             case 37: // left
24002             case 39: // right
24003                 this.onTogglePeriod();
24004                 break;
24005             case 38: // up
24006                 this.onIncrementMinutes();
24007                 break;
24008             case 40: // down
24009                 this.onDecrementMinutes();
24010                 break;
24011             case 13: // enter
24012             case 9: // tab
24013                 this.setTime();
24014                 break;
24015         }
24016     },
24017     
24018     onClick: function(e) {
24019         e.stopPropagation();
24020         e.preventDefault();
24021     },
24022     
24023     picker : function()
24024     {
24025         return this.pickerEl;
24026     },
24027     
24028     fillTime: function()
24029     {    
24030         var time = this.pop.select('tbody', true).first();
24031         
24032         time.dom.innerHTML = '';
24033         
24034         time.createChild({
24035             tag: 'tr',
24036             cn: [
24037                 {
24038                     tag: 'td',
24039                     cn: [
24040                         {
24041                             tag: 'a',
24042                             href: '#',
24043                             cls: 'btn',
24044                             cn: [
24045                                 {
24046                                     tag: 'i',
24047                                     cls: 'hours-up fa fas fa-chevron-up'
24048                                 }
24049                             ]
24050                         } 
24051                     ]
24052                 },
24053                 {
24054                     tag: 'td',
24055                     cls: 'separator'
24056                 },
24057                 {
24058                     tag: 'td',
24059                     cn: [
24060                         {
24061                             tag: 'a',
24062                             href: '#',
24063                             cls: 'btn',
24064                             cn: [
24065                                 {
24066                                     tag: 'i',
24067                                     cls: 'minutes-up fa fas fa-chevron-up'
24068                                 }
24069                             ]
24070                         }
24071                     ]
24072                 },
24073                 {
24074                     tag: 'td',
24075                     cls: 'separator'
24076                 }
24077             ]
24078         });
24079         
24080         time.createChild({
24081             tag: 'tr',
24082             cn: [
24083                 {
24084                     tag: 'td',
24085                     cn: [
24086                         {
24087                             tag: 'span',
24088                             cls: 'timepicker-hour',
24089                             html: '00'
24090                         }  
24091                     ]
24092                 },
24093                 {
24094                     tag: 'td',
24095                     cls: 'separator',
24096                     html: ':'
24097                 },
24098                 {
24099                     tag: 'td',
24100                     cn: [
24101                         {
24102                             tag: 'span',
24103                             cls: 'timepicker-minute',
24104                             html: '00'
24105                         }  
24106                     ]
24107                 },
24108                 {
24109                     tag: 'td',
24110                     cls: 'separator'
24111                 },
24112                 {
24113                     tag: 'td',
24114                     cn: [
24115                         {
24116                             tag: 'button',
24117                             type: 'button',
24118                             cls: 'btn btn-primary period',
24119                             html: 'AM'
24120                             
24121                         }
24122                     ]
24123                 }
24124             ]
24125         });
24126         
24127         time.createChild({
24128             tag: 'tr',
24129             cn: [
24130                 {
24131                     tag: 'td',
24132                     cn: [
24133                         {
24134                             tag: 'a',
24135                             href: '#',
24136                             cls: 'btn',
24137                             cn: [
24138                                 {
24139                                     tag: 'span',
24140                                     cls: 'hours-down fa fas fa-chevron-down'
24141                                 }
24142                             ]
24143                         }
24144                     ]
24145                 },
24146                 {
24147                     tag: 'td',
24148                     cls: 'separator'
24149                 },
24150                 {
24151                     tag: 'td',
24152                     cn: [
24153                         {
24154                             tag: 'a',
24155                             href: '#',
24156                             cls: 'btn',
24157                             cn: [
24158                                 {
24159                                     tag: 'span',
24160                                     cls: 'minutes-down fa fas fa-chevron-down'
24161                                 }
24162                             ]
24163                         }
24164                     ]
24165                 },
24166                 {
24167                     tag: 'td',
24168                     cls: 'separator'
24169                 }
24170             ]
24171         });
24172         
24173     },
24174     
24175     update: function()
24176     {
24177         
24178         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24179         
24180         this.fill();
24181     },
24182     
24183     fill: function() 
24184     {
24185         var hours = this.time.getHours();
24186         var minutes = this.time.getMinutes();
24187         var period = 'AM';
24188         
24189         if(hours > 11){
24190             period = 'PM';
24191         }
24192         
24193         if(hours == 0){
24194             hours = 12;
24195         }
24196         
24197         
24198         if(hours > 12){
24199             hours = hours - 12;
24200         }
24201         
24202         if(hours < 10){
24203             hours = '0' + hours;
24204         }
24205         
24206         if(minutes < 10){
24207             minutes = '0' + minutes;
24208         }
24209         
24210         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24211         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24212         this.pop.select('button', true).first().dom.innerHTML = period;
24213         
24214     },
24215     
24216     place: function()
24217     {   
24218         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24219         
24220         var cls = ['bottom'];
24221         
24222         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24223             cls.pop();
24224             cls.push('top');
24225         }
24226         
24227         cls.push('right');
24228         
24229         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24230             cls.pop();
24231             cls.push('left');
24232         }
24233         //this.picker().setXY(20000,20000);
24234         this.picker().addClass(cls.join('-'));
24235         
24236         var _this = this;
24237         
24238         Roo.each(cls, function(c){
24239             if(c == 'bottom'){
24240                 (function() {
24241                  //  
24242                 }).defer(200);
24243                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24244                 //_this.picker().setTop(_this.inputEl().getHeight());
24245                 return;
24246             }
24247             if(c == 'top'){
24248                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24249                 
24250                 //_this.picker().setTop(0 - _this.picker().getHeight());
24251                 return;
24252             }
24253             /*
24254             if(c == 'left'){
24255                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24256                 return;
24257             }
24258             if(c == 'right'){
24259                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24260                 return;
24261             }
24262             */
24263         });
24264         
24265     },
24266   
24267     onFocus : function()
24268     {
24269         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24270         this.show();
24271     },
24272     
24273     onBlur : function()
24274     {
24275         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24276         this.hide();
24277     },
24278     
24279     show : function()
24280     {
24281         this.picker().show();
24282         this.pop.show();
24283         this.update();
24284         this.place();
24285         
24286         this.fireEvent('show', this, this.date);
24287     },
24288     
24289     hide : function()
24290     {
24291         this.picker().hide();
24292         this.pop.hide();
24293         
24294         this.fireEvent('hide', this, this.date);
24295     },
24296     
24297     setTime : function()
24298     {
24299         this.hide();
24300         this.setValue(this.time.format(this.format));
24301         
24302         this.fireEvent('select', this, this.date);
24303         
24304         
24305     },
24306     
24307     onMousedown: function(e){
24308         e.stopPropagation();
24309         e.preventDefault();
24310     },
24311     
24312     onIncrementHours: function()
24313     {
24314         Roo.log('onIncrementHours');
24315         this.time = this.time.add(Date.HOUR, 1);
24316         this.update();
24317         
24318     },
24319     
24320     onDecrementHours: function()
24321     {
24322         Roo.log('onDecrementHours');
24323         this.time = this.time.add(Date.HOUR, -1);
24324         this.update();
24325     },
24326     
24327     onIncrementMinutes: function()
24328     {
24329         Roo.log('onIncrementMinutes');
24330         this.time = this.time.add(Date.MINUTE, 1);
24331         this.update();
24332     },
24333     
24334     onDecrementMinutes: function()
24335     {
24336         Roo.log('onDecrementMinutes');
24337         this.time = this.time.add(Date.MINUTE, -1);
24338         this.update();
24339     },
24340     
24341     onTogglePeriod: function()
24342     {
24343         Roo.log('onTogglePeriod');
24344         this.time = this.time.add(Date.HOUR, 12);
24345         this.update();
24346     }
24347     
24348    
24349 });
24350  
24351
24352 Roo.apply(Roo.bootstrap.form.TimeField,  {
24353   
24354     template : {
24355         tag: 'div',
24356         cls: 'datepicker dropdown-menu',
24357         cn: [
24358             {
24359                 tag: 'div',
24360                 cls: 'datepicker-time',
24361                 cn: [
24362                 {
24363                     tag: 'table',
24364                     cls: 'table-condensed',
24365                     cn:[
24366                         {
24367                             tag: 'tbody',
24368                             cn: [
24369                                 {
24370                                     tag: 'tr',
24371                                     cn: [
24372                                     {
24373                                         tag: 'td',
24374                                         colspan: '7'
24375                                     }
24376                                     ]
24377                                 }
24378                             ]
24379                         },
24380                         {
24381                             tag: 'tfoot',
24382                             cn: [
24383                                 {
24384                                     tag: 'tr',
24385                                     cn: [
24386                                     {
24387                                         tag: 'th',
24388                                         colspan: '7',
24389                                         cls: '',
24390                                         cn: [
24391                                             {
24392                                                 tag: 'button',
24393                                                 cls: 'btn btn-info ok',
24394                                                 html: 'OK'
24395                                             }
24396                                         ]
24397                                     }
24398                     
24399                                     ]
24400                                 }
24401                             ]
24402                         }
24403                     ]
24404                 }
24405                 ]
24406             }
24407         ]
24408     }
24409 });
24410
24411  
24412
24413  /*
24414  * - LGPL
24415  *
24416  * MonthField
24417  * 
24418  */
24419
24420 /**
24421  * @class Roo.bootstrap.form.MonthField
24422  * @extends Roo.bootstrap.form.Input
24423  * Bootstrap MonthField class
24424  * 
24425  * @cfg {String} language default en
24426  * 
24427  * @constructor
24428  * Create a new MonthField
24429  * @param {Object} config The config object
24430  */
24431
24432 Roo.bootstrap.form.MonthField = function(config){
24433     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24434     
24435     this.addEvents({
24436         /**
24437          * @event show
24438          * Fires when this field show.
24439          * @param {Roo.bootstrap.form.MonthField} this
24440          * @param {Mixed} date The date value
24441          */
24442         show : true,
24443         /**
24444          * @event show
24445          * Fires when this field hide.
24446          * @param {Roo.bootstrap.form.MonthField} this
24447          * @param {Mixed} date The date value
24448          */
24449         hide : true,
24450         /**
24451          * @event select
24452          * Fires when select a date.
24453          * @param {Roo.bootstrap.form.MonthField} this
24454          * @param {String} oldvalue The old value
24455          * @param {String} newvalue The new value
24456          */
24457         select : true
24458     });
24459 };
24460
24461 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24462     
24463     onRender: function(ct, position)
24464     {
24465         
24466         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24467         
24468         this.language = this.language || 'en';
24469         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24470         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24471         
24472         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24473         this.isInline = false;
24474         this.isInput = true;
24475         this.component = this.el.select('.add-on', true).first() || false;
24476         this.component = (this.component && this.component.length === 0) ? false : this.component;
24477         this.hasInput = this.component && this.inputEL().length;
24478         
24479         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24480         
24481         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24482         
24483         this.picker().on('mousedown', this.onMousedown, this);
24484         this.picker().on('click', this.onClick, this);
24485         
24486         this.picker().addClass('datepicker-dropdown');
24487         
24488         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24489             v.setStyle('width', '189px');
24490         });
24491         
24492         this.fillMonths();
24493         
24494         this.update();
24495         
24496         if(this.isInline) {
24497             this.show();
24498         }
24499         
24500     },
24501     
24502     setValue: function(v, suppressEvent)
24503     {   
24504         var o = this.getValue();
24505         
24506         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24507         
24508         this.update();
24509
24510         if(suppressEvent !== true){
24511             this.fireEvent('select', this, o, v);
24512         }
24513         
24514     },
24515     
24516     getValue: function()
24517     {
24518         return this.value;
24519     },
24520     
24521     onClick: function(e) 
24522     {
24523         e.stopPropagation();
24524         e.preventDefault();
24525         
24526         var target = e.getTarget();
24527         
24528         if(target.nodeName.toLowerCase() === 'i'){
24529             target = Roo.get(target).dom.parentNode;
24530         }
24531         
24532         var nodeName = target.nodeName;
24533         var className = target.className;
24534         var html = target.innerHTML;
24535         
24536         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24537             return;
24538         }
24539         
24540         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24541         
24542         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24543         
24544         this.hide();
24545                         
24546     },
24547     
24548     picker : function()
24549     {
24550         return this.pickerEl;
24551     },
24552     
24553     fillMonths: function()
24554     {    
24555         var i = 0;
24556         var months = this.picker().select('>.datepicker-months td', true).first();
24557         
24558         months.dom.innerHTML = '';
24559         
24560         while (i < 12) {
24561             var month = {
24562                 tag: 'span',
24563                 cls: 'month',
24564                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24565             };
24566             
24567             months.createChild(month);
24568         }
24569         
24570     },
24571     
24572     update: function()
24573     {
24574         var _this = this;
24575         
24576         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24577             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24578         }
24579         
24580         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24581             e.removeClass('active');
24582             
24583             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24584                 e.addClass('active');
24585             }
24586         })
24587     },
24588     
24589     place: function()
24590     {
24591         if(this.isInline) {
24592             return;
24593         }
24594         
24595         this.picker().removeClass(['bottom', 'top']);
24596         
24597         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24598             /*
24599              * place to the top of element!
24600              *
24601              */
24602             
24603             this.picker().addClass('top');
24604             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24605             
24606             return;
24607         }
24608         
24609         this.picker().addClass('bottom');
24610         
24611         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24612     },
24613     
24614     onFocus : function()
24615     {
24616         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24617         this.show();
24618     },
24619     
24620     onBlur : function()
24621     {
24622         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24623         
24624         var d = this.inputEl().getValue();
24625         
24626         this.setValue(d);
24627                 
24628         this.hide();
24629     },
24630     
24631     show : function()
24632     {
24633         this.picker().show();
24634         this.picker().select('>.datepicker-months', true).first().show();
24635         this.update();
24636         this.place();
24637         
24638         this.fireEvent('show', this, this.date);
24639     },
24640     
24641     hide : function()
24642     {
24643         if(this.isInline) {
24644             return;
24645         }
24646         this.picker().hide();
24647         this.fireEvent('hide', this, this.date);
24648         
24649     },
24650     
24651     onMousedown: function(e)
24652     {
24653         e.stopPropagation();
24654         e.preventDefault();
24655     },
24656     
24657     keyup: function(e)
24658     {
24659         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24660         this.update();
24661     },
24662
24663     fireKey: function(e)
24664     {
24665         if (!this.picker().isVisible()){
24666             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24667                 this.show();
24668             }
24669             return;
24670         }
24671         
24672         var dir;
24673         
24674         switch(e.keyCode){
24675             case 27: // escape
24676                 this.hide();
24677                 e.preventDefault();
24678                 break;
24679             case 37: // left
24680             case 39: // right
24681                 dir = e.keyCode == 37 ? -1 : 1;
24682                 
24683                 this.vIndex = this.vIndex + dir;
24684                 
24685                 if(this.vIndex < 0){
24686                     this.vIndex = 0;
24687                 }
24688                 
24689                 if(this.vIndex > 11){
24690                     this.vIndex = 11;
24691                 }
24692                 
24693                 if(isNaN(this.vIndex)){
24694                     this.vIndex = 0;
24695                 }
24696                 
24697                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24698                 
24699                 break;
24700             case 38: // up
24701             case 40: // down
24702                 
24703                 dir = e.keyCode == 38 ? -1 : 1;
24704                 
24705                 this.vIndex = this.vIndex + dir * 4;
24706                 
24707                 if(this.vIndex < 0){
24708                     this.vIndex = 0;
24709                 }
24710                 
24711                 if(this.vIndex > 11){
24712                     this.vIndex = 11;
24713                 }
24714                 
24715                 if(isNaN(this.vIndex)){
24716                     this.vIndex = 0;
24717                 }
24718                 
24719                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24720                 break;
24721                 
24722             case 13: // enter
24723                 
24724                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24725                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24726                 }
24727                 
24728                 this.hide();
24729                 e.preventDefault();
24730                 break;
24731             case 9: // tab
24732                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24733                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24734                 }
24735                 this.hide();
24736                 break;
24737             case 16: // shift
24738             case 17: // ctrl
24739             case 18: // alt
24740                 break;
24741             default :
24742                 this.hide();
24743                 
24744         }
24745     },
24746     
24747     remove: function() 
24748     {
24749         this.picker().remove();
24750     }
24751    
24752 });
24753
24754 Roo.apply(Roo.bootstrap.form.MonthField,  {
24755     
24756     content : {
24757         tag: 'tbody',
24758         cn: [
24759         {
24760             tag: 'tr',
24761             cn: [
24762             {
24763                 tag: 'td',
24764                 colspan: '7'
24765             }
24766             ]
24767         }
24768         ]
24769     },
24770     
24771     dates:{
24772         en: {
24773             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24774             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24775         }
24776     }
24777 });
24778
24779 Roo.apply(Roo.bootstrap.form.MonthField,  {
24780   
24781     template : {
24782         tag: 'div',
24783         cls: 'datepicker dropdown-menu roo-dynamic',
24784         cn: [
24785             {
24786                 tag: 'div',
24787                 cls: 'datepicker-months',
24788                 cn: [
24789                 {
24790                     tag: 'table',
24791                     cls: 'table-condensed',
24792                     cn:[
24793                         Roo.bootstrap.form.DateField.content
24794                     ]
24795                 }
24796                 ]
24797             }
24798         ]
24799     }
24800 });
24801
24802  
24803
24804  
24805  /*
24806  * - LGPL
24807  *
24808  * CheckBox
24809  * 
24810  */
24811
24812 /**
24813  * @class Roo.bootstrap.form.CheckBox
24814  * @extends Roo.bootstrap.form.Input
24815  * Bootstrap CheckBox class
24816  * 
24817  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24818  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24819  * @cfg {String} boxLabel The text that appears beside the checkbox
24820  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24821  * @cfg {Boolean} checked initnal the element
24822  * @cfg {Boolean} inline inline the element (default false)
24823  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24824  * @cfg {String} tooltip label tooltip
24825  * 
24826  * @constructor
24827  * Create a new CheckBox
24828  * @param {Object} config The config object
24829  */
24830
24831 Roo.bootstrap.form.CheckBox = function(config){
24832     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24833    
24834     this.addEvents({
24835         /**
24836         * @event check
24837         * Fires when the element is checked or unchecked.
24838         * @param {Roo.bootstrap.form.CheckBox} this This input
24839         * @param {Boolean} checked The new checked value
24840         */
24841        check : true,
24842        /**
24843         * @event click
24844         * Fires when the element is click.
24845         * @param {Roo.bootstrap.form.CheckBox} this This input
24846         */
24847        click : true
24848     });
24849     
24850 };
24851
24852 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24853   
24854     inputType: 'checkbox',
24855     inputValue: 1,
24856     valueOff: 0,
24857     boxLabel: false,
24858     checked: false,
24859     weight : false,
24860     inline: false,
24861     tooltip : '',
24862     
24863     // checkbox success does not make any sense really.. 
24864     invalidClass : "",
24865     validClass : "",
24866     
24867     
24868     getAutoCreate : function()
24869     {
24870         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24871         
24872         var id = Roo.id();
24873         
24874         var cfg = {};
24875         
24876         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24877         
24878         if(this.inline){
24879             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24880         }
24881         
24882         var input =  {
24883             tag: 'input',
24884             id : id,
24885             type : this.inputType,
24886             value : this.inputValue,
24887             cls : 'roo-' + this.inputType, //'form-box',
24888             placeholder : this.placeholder || ''
24889             
24890         };
24891         
24892         if(this.inputType != 'radio'){
24893             var hidden =  {
24894                 tag: 'input',
24895                 type : 'hidden',
24896                 cls : 'roo-hidden-value',
24897                 value : this.checked ? this.inputValue : this.valueOff
24898             };
24899         }
24900         
24901             
24902         if (this.weight) { // Validity check?
24903             cfg.cls += " " + this.inputType + "-" + this.weight;
24904         }
24905         
24906         if (this.disabled) {
24907             input.disabled=true;
24908         }
24909         
24910         if(this.checked){
24911             input.checked = this.checked;
24912         }
24913         
24914         if (this.name) {
24915             
24916             input.name = this.name;
24917             
24918             if(this.inputType != 'radio'){
24919                 hidden.name = this.name;
24920                 input.name = '_hidden_' + this.name;
24921             }
24922         }
24923         
24924         if (this.size) {
24925             input.cls += ' input-' + this.size;
24926         }
24927         
24928         var settings=this;
24929         
24930         ['xs','sm','md','lg'].map(function(size){
24931             if (settings[size]) {
24932                 cfg.cls += ' col-' + size + '-' + settings[size];
24933             }
24934         });
24935         
24936         var inputblock = input;
24937          
24938         if (this.before || this.after) {
24939             
24940             inputblock = {
24941                 cls : 'input-group',
24942                 cn :  [] 
24943             };
24944             
24945             if (this.before) {
24946                 inputblock.cn.push({
24947                     tag :'span',
24948                     cls : 'input-group-addon',
24949                     html : this.before
24950                 });
24951             }
24952             
24953             inputblock.cn.push(input);
24954             
24955             if(this.inputType != 'radio'){
24956                 inputblock.cn.push(hidden);
24957             }
24958             
24959             if (this.after) {
24960                 inputblock.cn.push({
24961                     tag :'span',
24962                     cls : 'input-group-addon',
24963                     html : this.after
24964                 });
24965             }
24966             
24967         }
24968         var boxLabelCfg = false;
24969         
24970         if(this.boxLabel){
24971            
24972             boxLabelCfg = {
24973                 tag: 'label',
24974                 //'for': id, // box label is handled by onclick - so no for...
24975                 cls: 'box-label',
24976                 html: this.boxLabel
24977             };
24978             if(this.tooltip){
24979                 boxLabelCfg.tooltip = this.tooltip;
24980             }
24981              
24982         }
24983         
24984         
24985         if (align ==='left' && this.fieldLabel.length) {
24986 //                Roo.log("left and has label");
24987             cfg.cn = [
24988                 {
24989                     tag: 'label',
24990                     'for' :  id,
24991                     cls : 'control-label',
24992                     html : this.fieldLabel
24993                 },
24994                 {
24995                     cls : "", 
24996                     cn: [
24997                         inputblock
24998                     ]
24999                 }
25000             ];
25001             
25002             if (boxLabelCfg) {
25003                 cfg.cn[1].cn.push(boxLabelCfg);
25004             }
25005             
25006             if(this.labelWidth > 12){
25007                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25008             }
25009             
25010             if(this.labelWidth < 13 && this.labelmd == 0){
25011                 this.labelmd = this.labelWidth;
25012             }
25013             
25014             if(this.labellg > 0){
25015                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25016                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25017             }
25018             
25019             if(this.labelmd > 0){
25020                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25021                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25022             }
25023             
25024             if(this.labelsm > 0){
25025                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25026                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25027             }
25028             
25029             if(this.labelxs > 0){
25030                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25031                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25032             }
25033             
25034         } else if ( this.fieldLabel.length) {
25035 //                Roo.log(" label");
25036                 cfg.cn = [
25037                    
25038                     {
25039                         tag: this.boxLabel ? 'span' : 'label',
25040                         'for': id,
25041                         cls: 'control-label box-input-label',
25042                         //cls : 'input-group-addon',
25043                         html : this.fieldLabel
25044                     },
25045                     
25046                     inputblock
25047                     
25048                 ];
25049                 if (boxLabelCfg) {
25050                     cfg.cn.push(boxLabelCfg);
25051                 }
25052
25053         } else {
25054             
25055 //                Roo.log(" no label && no align");
25056                 cfg.cn = [  inputblock ] ;
25057                 if (boxLabelCfg) {
25058                     cfg.cn.push(boxLabelCfg);
25059                 }
25060
25061                 
25062         }
25063         
25064        
25065         
25066         if(this.inputType != 'radio'){
25067             cfg.cn.push(hidden);
25068         }
25069         
25070         return cfg;
25071         
25072     },
25073     
25074     /**
25075      * return the real input element.
25076      */
25077     inputEl: function ()
25078     {
25079         return this.el.select('input.roo-' + this.inputType,true).first();
25080     },
25081     hiddenEl: function ()
25082     {
25083         return this.el.select('input.roo-hidden-value',true).first();
25084     },
25085     
25086     labelEl: function()
25087     {
25088         return this.el.select('label.control-label',true).first();
25089     },
25090     /* depricated... */
25091     
25092     label: function()
25093     {
25094         return this.labelEl();
25095     },
25096     
25097     boxLabelEl: function()
25098     {
25099         return this.el.select('label.box-label',true).first();
25100     },
25101     
25102     initEvents : function()
25103     {
25104 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25105         
25106         this.inputEl().on('click', this.onClick,  this);
25107         
25108         if (this.boxLabel) { 
25109             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25110         }
25111         
25112         this.startValue = this.getValue();
25113         
25114         if(this.groupId){
25115             Roo.bootstrap.form.CheckBox.register(this);
25116         }
25117     },
25118     
25119     onClick : function(e)
25120     {   
25121         if(this.fireEvent('click', this, e) !== false){
25122             this.setChecked(!this.checked);
25123         }
25124         
25125     },
25126     
25127     setChecked : function(state,suppressEvent)
25128     {
25129         this.startValue = this.getValue();
25130
25131         if(this.inputType == 'radio'){
25132             
25133             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25134                 e.dom.checked = false;
25135             });
25136             
25137             this.inputEl().dom.checked = true;
25138             
25139             this.inputEl().dom.value = this.inputValue;
25140             
25141             if(suppressEvent !== true){
25142                 this.fireEvent('check', this, true);
25143             }
25144             
25145             this.validate();
25146             
25147             return;
25148         }
25149         
25150         this.checked = state;
25151         
25152         this.inputEl().dom.checked = state;
25153         
25154         
25155         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25156         
25157         if(suppressEvent !== true){
25158             this.fireEvent('check', this, state);
25159         }
25160         
25161         this.validate();
25162     },
25163     
25164     getValue : function()
25165     {
25166         if(this.inputType == 'radio'){
25167             return this.getGroupValue();
25168         }
25169         
25170         return this.hiddenEl().dom.value;
25171         
25172     },
25173     
25174     getGroupValue : function()
25175     {
25176         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25177             return '';
25178         }
25179         
25180         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25181     },
25182     
25183     setValue : function(v,suppressEvent)
25184     {
25185         if(this.inputType == 'radio'){
25186             this.setGroupValue(v, suppressEvent);
25187             return;
25188         }
25189         
25190         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25191         
25192         this.validate();
25193     },
25194     
25195     setGroupValue : function(v, suppressEvent)
25196     {
25197         this.startValue = this.getValue();
25198         
25199         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25200             e.dom.checked = false;
25201             
25202             if(e.dom.value == v){
25203                 e.dom.checked = true;
25204             }
25205         });
25206         
25207         if(suppressEvent !== true){
25208             this.fireEvent('check', this, true);
25209         }
25210
25211         this.validate();
25212         
25213         return;
25214     },
25215     
25216     validate : function()
25217     {
25218         if(this.getVisibilityEl().hasClass('hidden')){
25219             return true;
25220         }
25221         
25222         if(
25223                 this.disabled || 
25224                 (this.inputType == 'radio' && this.validateRadio()) ||
25225                 (this.inputType == 'checkbox' && this.validateCheckbox())
25226         ){
25227             this.markValid();
25228             return true;
25229         }
25230         
25231         this.markInvalid();
25232         return false;
25233     },
25234     
25235     validateRadio : function()
25236     {
25237         if(this.getVisibilityEl().hasClass('hidden')){
25238             return true;
25239         }
25240         
25241         if(this.allowBlank){
25242             return true;
25243         }
25244         
25245         var valid = false;
25246         
25247         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25248             if(!e.dom.checked){
25249                 return;
25250             }
25251             
25252             valid = true;
25253             
25254             return false;
25255         });
25256         
25257         return valid;
25258     },
25259     
25260     validateCheckbox : function()
25261     {
25262         if(!this.groupId){
25263             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25264             //return (this.getValue() == this.inputValue) ? true : false;
25265         }
25266         
25267         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25268         
25269         if(!group){
25270             return false;
25271         }
25272         
25273         var r = false;
25274         
25275         for(var i in group){
25276             if(group[i].el.isVisible(true)){
25277                 r = false;
25278                 break;
25279             }
25280             
25281             r = true;
25282         }
25283         
25284         for(var i in group){
25285             if(r){
25286                 break;
25287             }
25288             
25289             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25290         }
25291         
25292         return r;
25293     },
25294     
25295     /**
25296      * Mark this field as valid
25297      */
25298     markValid : function()
25299     {
25300         var _this = this;
25301         
25302         this.fireEvent('valid', this);
25303         
25304         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25305         
25306         if(this.groupId){
25307             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25308         }
25309         
25310         if(label){
25311             label.markValid();
25312         }
25313
25314         if(this.inputType == 'radio'){
25315             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25316                 var fg = e.findParent('.form-group', false, true);
25317                 if (Roo.bootstrap.version == 3) {
25318                     fg.removeClass([_this.invalidClass, _this.validClass]);
25319                     fg.addClass(_this.validClass);
25320                 } else {
25321                     fg.removeClass(['is-valid', 'is-invalid']);
25322                     fg.addClass('is-valid');
25323                 }
25324             });
25325             
25326             return;
25327         }
25328
25329         if(!this.groupId){
25330             var fg = this.el.findParent('.form-group', false, true);
25331             if (Roo.bootstrap.version == 3) {
25332                 fg.removeClass([this.invalidClass, this.validClass]);
25333                 fg.addClass(this.validClass);
25334             } else {
25335                 fg.removeClass(['is-valid', 'is-invalid']);
25336                 fg.addClass('is-valid');
25337             }
25338             return;
25339         }
25340         
25341         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25342         
25343         if(!group){
25344             return;
25345         }
25346         
25347         for(var i in group){
25348             var fg = group[i].el.findParent('.form-group', false, true);
25349             if (Roo.bootstrap.version == 3) {
25350                 fg.removeClass([this.invalidClass, this.validClass]);
25351                 fg.addClass(this.validClass);
25352             } else {
25353                 fg.removeClass(['is-valid', 'is-invalid']);
25354                 fg.addClass('is-valid');
25355             }
25356         }
25357     },
25358     
25359      /**
25360      * Mark this field as invalid
25361      * @param {String} msg The validation message
25362      */
25363     markInvalid : function(msg)
25364     {
25365         if(this.allowBlank){
25366             return;
25367         }
25368         
25369         var _this = this;
25370         
25371         this.fireEvent('invalid', this, msg);
25372         
25373         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25374         
25375         if(this.groupId){
25376             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25377         }
25378         
25379         if(label){
25380             label.markInvalid();
25381         }
25382             
25383         if(this.inputType == 'radio'){
25384             
25385             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25386                 var fg = e.findParent('.form-group', false, true);
25387                 if (Roo.bootstrap.version == 3) {
25388                     fg.removeClass([_this.invalidClass, _this.validClass]);
25389                     fg.addClass(_this.invalidClass);
25390                 } else {
25391                     fg.removeClass(['is-invalid', 'is-valid']);
25392                     fg.addClass('is-invalid');
25393                 }
25394             });
25395             
25396             return;
25397         }
25398         
25399         if(!this.groupId){
25400             var fg = this.el.findParent('.form-group', false, true);
25401             if (Roo.bootstrap.version == 3) {
25402                 fg.removeClass([_this.invalidClass, _this.validClass]);
25403                 fg.addClass(_this.invalidClass);
25404             } else {
25405                 fg.removeClass(['is-invalid', 'is-valid']);
25406                 fg.addClass('is-invalid');
25407             }
25408             return;
25409         }
25410         
25411         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25412         
25413         if(!group){
25414             return;
25415         }
25416         
25417         for(var i in group){
25418             var fg = group[i].el.findParent('.form-group', false, true);
25419             if (Roo.bootstrap.version == 3) {
25420                 fg.removeClass([_this.invalidClass, _this.validClass]);
25421                 fg.addClass(_this.invalidClass);
25422             } else {
25423                 fg.removeClass(['is-invalid', 'is-valid']);
25424                 fg.addClass('is-invalid');
25425             }
25426         }
25427         
25428     },
25429     
25430     clearInvalid : function()
25431     {
25432         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25433         
25434         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25435         
25436         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25437         
25438         if (label && label.iconEl) {
25439             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25440             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25441         }
25442     },
25443     
25444     disable : function()
25445     {
25446         if(this.inputType != 'radio'){
25447             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25448             return;
25449         }
25450         
25451         var _this = this;
25452         
25453         if(this.rendered){
25454             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25455                 _this.getActionEl().addClass(this.disabledClass);
25456                 e.dom.disabled = true;
25457             });
25458         }
25459         
25460         this.disabled = true;
25461         this.fireEvent("disable", this);
25462         return this;
25463     },
25464
25465     enable : function()
25466     {
25467         if(this.inputType != 'radio'){
25468             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25469             return;
25470         }
25471         
25472         var _this = this;
25473         
25474         if(this.rendered){
25475             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25476                 _this.getActionEl().removeClass(this.disabledClass);
25477                 e.dom.disabled = false;
25478             });
25479         }
25480         
25481         this.disabled = false;
25482         this.fireEvent("enable", this);
25483         return this;
25484     },
25485     
25486     setBoxLabel : function(v)
25487     {
25488         this.boxLabel = v;
25489         
25490         if(this.rendered){
25491             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25492         }
25493     }
25494
25495 });
25496
25497 Roo.apply(Roo.bootstrap.form.CheckBox, {
25498     
25499     groups: {},
25500     
25501      /**
25502     * register a CheckBox Group
25503     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25504     */
25505     register : function(checkbox)
25506     {
25507         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25508             this.groups[checkbox.groupId] = {};
25509         }
25510         
25511         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25512             return;
25513         }
25514         
25515         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25516         
25517     },
25518     /**
25519     * fetch a CheckBox Group based on the group ID
25520     * @param {string} the group ID
25521     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25522     */
25523     get: function(groupId) {
25524         if (typeof(this.groups[groupId]) == 'undefined') {
25525             return false;
25526         }
25527         
25528         return this.groups[groupId] ;
25529     }
25530     
25531     
25532 });
25533 /*
25534  * - LGPL
25535  *
25536  * RadioItem
25537  * 
25538  */
25539
25540 /**
25541  * @class Roo.bootstrap.form.Radio
25542  * @extends Roo.bootstrap.Component
25543  * Bootstrap Radio class
25544  * @cfg {String} boxLabel - the label associated
25545  * @cfg {String} value - the value of radio
25546  * 
25547  * @constructor
25548  * Create a new Radio
25549  * @param {Object} config The config object
25550  */
25551 Roo.bootstrap.form.Radio = function(config){
25552     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25553     
25554 };
25555
25556 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25557     
25558     boxLabel : '',
25559     
25560     value : '',
25561     
25562     getAutoCreate : function()
25563     {
25564         var cfg = {
25565             tag : 'div',
25566             cls : 'form-group radio',
25567             cn : [
25568                 {
25569                     tag : 'label',
25570                     cls : 'box-label',
25571                     html : this.boxLabel
25572                 }
25573             ]
25574         };
25575         
25576         return cfg;
25577     },
25578     
25579     initEvents : function() 
25580     {
25581         this.parent().register(this);
25582         
25583         this.el.on('click', this.onClick, this);
25584         
25585     },
25586     
25587     onClick : function(e)
25588     {
25589         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25590             this.setChecked(true);
25591         }
25592     },
25593     
25594     setChecked : function(state, suppressEvent)
25595     {
25596         this.parent().setValue(this.value, suppressEvent);
25597         
25598     },
25599     
25600     setBoxLabel : function(v)
25601     {
25602         this.boxLabel = v;
25603         
25604         if(this.rendered){
25605             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25606         }
25607     }
25608     
25609 });
25610  
25611
25612  /*
25613  * - LGPL
25614  *
25615  * Input
25616  * 
25617  */
25618
25619 /**
25620  * @class Roo.bootstrap.form.SecurePass
25621  * @extends Roo.bootstrap.form.Input
25622  * Bootstrap SecurePass class
25623  *
25624  * 
25625  * @constructor
25626  * Create a new SecurePass
25627  * @param {Object} config The config object
25628  */
25629  
25630 Roo.bootstrap.form.SecurePass = function (config) {
25631     // these go here, so the translation tool can replace them..
25632     this.errors = {
25633         PwdEmpty: "Please type a password, and then retype it to confirm.",
25634         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25635         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25636         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25637         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25638         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25639         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25640         TooWeak: "Your password is Too Weak."
25641     },
25642     this.meterLabel = "Password strength:";
25643     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25644     this.meterClass = [
25645         "roo-password-meter-tooweak", 
25646         "roo-password-meter-weak", 
25647         "roo-password-meter-medium", 
25648         "roo-password-meter-strong", 
25649         "roo-password-meter-grey"
25650     ];
25651     
25652     this.errors = {};
25653     
25654     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25655 }
25656
25657 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25658     /**
25659      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25660      * {
25661      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25662      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25663      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25664      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25665      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25666      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25667      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25668      * })
25669      */
25670     // private
25671     
25672     meterWidth: 300,
25673     errorMsg :'',    
25674     errors: false,
25675     imageRoot: '/',
25676     /**
25677      * @cfg {String/Object} Label for the strength meter (defaults to
25678      * 'Password strength:')
25679      */
25680     // private
25681     meterLabel: '',
25682     /**
25683      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25684      * ['Weak', 'Medium', 'Strong'])
25685      */
25686     // private    
25687     pwdStrengths: false,    
25688     // private
25689     strength: 0,
25690     // private
25691     _lastPwd: null,
25692     // private
25693     kCapitalLetter: 0,
25694     kSmallLetter: 1,
25695     kDigit: 2,
25696     kPunctuation: 3,
25697     
25698     insecure: false,
25699     // private
25700     initEvents: function ()
25701     {
25702         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25703
25704         if (this.el.is('input[type=password]') && Roo.isSafari) {
25705             this.el.on('keydown', this.SafariOnKeyDown, this);
25706         }
25707
25708         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25709     },
25710     // private
25711     onRender: function (ct, position)
25712     {
25713         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25714         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25715         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25716
25717         this.trigger.createChild({
25718                    cn: [
25719                     {
25720                     //id: 'PwdMeter',
25721                     tag: 'div',
25722                     cls: 'roo-password-meter-grey col-xs-12',
25723                     style: {
25724                         //width: 0,
25725                         //width: this.meterWidth + 'px'                                                
25726                         }
25727                     },
25728                     {                            
25729                          cls: 'roo-password-meter-text'                          
25730                     }
25731                 ]            
25732         });
25733
25734          
25735         if (this.hideTrigger) {
25736             this.trigger.setDisplayed(false);
25737         }
25738         this.setSize(this.width || '', this.height || '');
25739     },
25740     // private
25741     onDestroy: function ()
25742     {
25743         if (this.trigger) {
25744             this.trigger.removeAllListeners();
25745             this.trigger.remove();
25746         }
25747         if (this.wrap) {
25748             this.wrap.remove();
25749         }
25750         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25751     },
25752     // private
25753     checkStrength: function ()
25754     {
25755         var pwd = this.inputEl().getValue();
25756         if (pwd == this._lastPwd) {
25757             return;
25758         }
25759
25760         var strength;
25761         if (this.ClientSideStrongPassword(pwd)) {
25762             strength = 3;
25763         } else if (this.ClientSideMediumPassword(pwd)) {
25764             strength = 2;
25765         } else if (this.ClientSideWeakPassword(pwd)) {
25766             strength = 1;
25767         } else {
25768             strength = 0;
25769         }
25770         
25771         Roo.log('strength1: ' + strength);
25772         
25773         //var pm = this.trigger.child('div/div/div').dom;
25774         var pm = this.trigger.child('div/div');
25775         pm.removeClass(this.meterClass);
25776         pm.addClass(this.meterClass[strength]);
25777                 
25778         
25779         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25780                 
25781         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25782         
25783         this._lastPwd = pwd;
25784     },
25785     reset: function ()
25786     {
25787         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25788         
25789         this._lastPwd = '';
25790         
25791         var pm = this.trigger.child('div/div');
25792         pm.removeClass(this.meterClass);
25793         pm.addClass('roo-password-meter-grey');        
25794         
25795         
25796         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25797         
25798         pt.innerHTML = '';
25799         this.inputEl().dom.type='password';
25800     },
25801     // private
25802     validateValue: function (value)
25803     {
25804         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25805             return false;
25806         }
25807         if (value.length == 0) {
25808             if (this.allowBlank) {
25809                 this.clearInvalid();
25810                 return true;
25811             }
25812
25813             this.markInvalid(this.errors.PwdEmpty);
25814             this.errorMsg = this.errors.PwdEmpty;
25815             return false;
25816         }
25817         
25818         if(this.insecure){
25819             return true;
25820         }
25821         
25822         if (!value.match(/[\x21-\x7e]+/)) {
25823             this.markInvalid(this.errors.PwdBadChar);
25824             this.errorMsg = this.errors.PwdBadChar;
25825             return false;
25826         }
25827         if (value.length < 6) {
25828             this.markInvalid(this.errors.PwdShort);
25829             this.errorMsg = this.errors.PwdShort;
25830             return false;
25831         }
25832         if (value.length > 16) {
25833             this.markInvalid(this.errors.PwdLong);
25834             this.errorMsg = this.errors.PwdLong;
25835             return false;
25836         }
25837         var strength;
25838         if (this.ClientSideStrongPassword(value)) {
25839             strength = 3;
25840         } else if (this.ClientSideMediumPassword(value)) {
25841             strength = 2;
25842         } else if (this.ClientSideWeakPassword(value)) {
25843             strength = 1;
25844         } else {
25845             strength = 0;
25846         }
25847
25848         
25849         if (strength < 2) {
25850             //this.markInvalid(this.errors.TooWeak);
25851             this.errorMsg = this.errors.TooWeak;
25852             //return false;
25853         }
25854         
25855         
25856         console.log('strength2: ' + strength);
25857         
25858         //var pm = this.trigger.child('div/div/div').dom;
25859         
25860         var pm = this.trigger.child('div/div');
25861         pm.removeClass(this.meterClass);
25862         pm.addClass(this.meterClass[strength]);
25863                 
25864         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25865                 
25866         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25867         
25868         this.errorMsg = ''; 
25869         return true;
25870     },
25871     // private
25872     CharacterSetChecks: function (type)
25873     {
25874         this.type = type;
25875         this.fResult = false;
25876     },
25877     // private
25878     isctype: function (character, type)
25879     {
25880         switch (type) {  
25881             case this.kCapitalLetter:
25882                 if (character >= 'A' && character <= 'Z') {
25883                     return true;
25884                 }
25885                 break;
25886             
25887             case this.kSmallLetter:
25888                 if (character >= 'a' && character <= 'z') {
25889                     return true;
25890                 }
25891                 break;
25892             
25893             case this.kDigit:
25894                 if (character >= '0' && character <= '9') {
25895                     return true;
25896                 }
25897                 break;
25898             
25899             case this.kPunctuation:
25900                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25901                     return true;
25902                 }
25903                 break;
25904             
25905             default:
25906                 return false;
25907         }
25908
25909     },
25910     // private
25911     IsLongEnough: function (pwd, size)
25912     {
25913         return !(pwd == null || isNaN(size) || pwd.length < size);
25914     },
25915     // private
25916     SpansEnoughCharacterSets: function (word, nb)
25917     {
25918         if (!this.IsLongEnough(word, nb))
25919         {
25920             return false;
25921         }
25922
25923         var characterSetChecks = new Array(
25924             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25925             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25926         );
25927         
25928         for (var index = 0; index < word.length; ++index) {
25929             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25930                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25931                     characterSetChecks[nCharSet].fResult = true;
25932                     break;
25933                 }
25934             }
25935         }
25936
25937         var nCharSets = 0;
25938         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25939             if (characterSetChecks[nCharSet].fResult) {
25940                 ++nCharSets;
25941             }
25942         }
25943
25944         if (nCharSets < nb) {
25945             return false;
25946         }
25947         return true;
25948     },
25949     // private
25950     ClientSideStrongPassword: function (pwd)
25951     {
25952         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25953     },
25954     // private
25955     ClientSideMediumPassword: function (pwd)
25956     {
25957         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25958     },
25959     // private
25960     ClientSideWeakPassword: function (pwd)
25961     {
25962         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25963     }
25964           
25965 })//<script type="text/javascript">
25966
25967 /*
25968  * Based  Ext JS Library 1.1.1
25969  * Copyright(c) 2006-2007, Ext JS, LLC.
25970  * LGPL
25971  *
25972  */
25973  
25974 /**
25975  * @class Roo.HtmlEditorCore
25976  * @extends Roo.Component
25977  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25978  *
25979  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25980  */
25981
25982 Roo.HtmlEditorCore = function(config){
25983     
25984     
25985     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25986     
25987     
25988     this.addEvents({
25989         /**
25990          * @event initialize
25991          * Fires when the editor is fully initialized (including the iframe)
25992          * @param {Roo.HtmlEditorCore} this
25993          */
25994         initialize: true,
25995         /**
25996          * @event activate
25997          * Fires when the editor is first receives the focus. Any insertion must wait
25998          * until after this event.
25999          * @param {Roo.HtmlEditorCore} this
26000          */
26001         activate: true,
26002          /**
26003          * @event beforesync
26004          * Fires before the textarea is updated with content from the editor iframe. Return false
26005          * to cancel the sync.
26006          * @param {Roo.HtmlEditorCore} this
26007          * @param {String} html
26008          */
26009         beforesync: true,
26010          /**
26011          * @event beforepush
26012          * Fires before the iframe editor is updated with content from the textarea. Return false
26013          * to cancel the push.
26014          * @param {Roo.HtmlEditorCore} this
26015          * @param {String} html
26016          */
26017         beforepush: true,
26018          /**
26019          * @event sync
26020          * Fires when the textarea is updated with content from the editor iframe.
26021          * @param {Roo.HtmlEditorCore} this
26022          * @param {String} html
26023          */
26024         sync: true,
26025          /**
26026          * @event push
26027          * Fires when the iframe editor is updated with content from the textarea.
26028          * @param {Roo.HtmlEditorCore} this
26029          * @param {String} html
26030          */
26031         push: true,
26032         
26033         /**
26034          * @event editorevent
26035          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26036          * @param {Roo.HtmlEditorCore} this
26037          */
26038         editorevent: true
26039         
26040     });
26041     
26042     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26043     
26044     // defaults : white / black...
26045     this.applyBlacklists();
26046     
26047     
26048     
26049 };
26050
26051
26052 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26053
26054
26055      /**
26056      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26057      */
26058     
26059     owner : false,
26060     
26061      /**
26062      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26063      *                        Roo.resizable.
26064      */
26065     resizable : false,
26066      /**
26067      * @cfg {Number} height (in pixels)
26068      */   
26069     height: 300,
26070    /**
26071      * @cfg {Number} width (in pixels)
26072      */   
26073     width: 500,
26074     
26075     /**
26076      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26077      * 
26078      */
26079     stylesheets: false,
26080     
26081     /**
26082      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26083      */
26084     allowComments: false,
26085     // id of frame..
26086     frameId: false,
26087     
26088     // private properties
26089     validationEvent : false,
26090     deferHeight: true,
26091     initialized : false,
26092     activated : false,
26093     sourceEditMode : false,
26094     onFocus : Roo.emptyFn,
26095     iframePad:3,
26096     hideMode:'offsets',
26097     
26098     clearUp: true,
26099     
26100     // blacklist + whitelisted elements..
26101     black: false,
26102     white: false,
26103      
26104     bodyCls : '',
26105
26106     /**
26107      * Protected method that will not generally be called directly. It
26108      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26109      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26110      */
26111     getDocMarkup : function(){
26112         // body styles..
26113         var st = '';
26114         
26115         // inherit styels from page...?? 
26116         if (this.stylesheets === false) {
26117             
26118             Roo.get(document.head).select('style').each(function(node) {
26119                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26120             });
26121             
26122             Roo.get(document.head).select('link').each(function(node) { 
26123                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26124             });
26125             
26126         } else if (!this.stylesheets.length) {
26127                 // simple..
26128                 st = '<style type="text/css">' +
26129                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26130                    '</style>';
26131         } else {
26132             for (var i in this.stylesheets) { 
26133                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26134             }
26135             
26136         }
26137         
26138         st +=  '<style type="text/css">' +
26139             'IMG { cursor: pointer } ' +
26140         '</style>';
26141
26142         var cls = 'roo-htmleditor-body';
26143         
26144         if(this.bodyCls.length){
26145             cls += ' ' + this.bodyCls;
26146         }
26147         
26148         return '<html><head>' + st  +
26149             //<style type="text/css">' +
26150             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26151             //'</style>' +
26152             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26153     },
26154
26155     // private
26156     onRender : function(ct, position)
26157     {
26158         var _t = this;
26159         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26160         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26161         
26162         
26163         this.el.dom.style.border = '0 none';
26164         this.el.dom.setAttribute('tabIndex', -1);
26165         this.el.addClass('x-hidden hide');
26166         
26167         
26168         
26169         if(Roo.isIE){ // fix IE 1px bogus margin
26170             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26171         }
26172        
26173         
26174         this.frameId = Roo.id();
26175         
26176          
26177         
26178         var iframe = this.owner.wrap.createChild({
26179             tag: 'iframe',
26180             cls: 'form-control', // bootstrap..
26181             id: this.frameId,
26182             name: this.frameId,
26183             frameBorder : 'no',
26184             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26185         }, this.el
26186         );
26187         
26188         
26189         this.iframe = iframe.dom;
26190
26191          this.assignDocWin();
26192         
26193         this.doc.designMode = 'on';
26194        
26195         this.doc.open();
26196         this.doc.write(this.getDocMarkup());
26197         this.doc.close();
26198
26199         
26200         var task = { // must defer to wait for browser to be ready
26201             run : function(){
26202                 //console.log("run task?" + this.doc.readyState);
26203                 this.assignDocWin();
26204                 if(this.doc.body || this.doc.readyState == 'complete'){
26205                     try {
26206                         this.doc.designMode="on";
26207                     } catch (e) {
26208                         return;
26209                     }
26210                     Roo.TaskMgr.stop(task);
26211                     this.initEditor.defer(10, this);
26212                 }
26213             },
26214             interval : 10,
26215             duration: 10000,
26216             scope: this
26217         };
26218         Roo.TaskMgr.start(task);
26219
26220     },
26221
26222     // private
26223     onResize : function(w, h)
26224     {
26225          Roo.log('resize: ' +w + ',' + h );
26226         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26227         if(!this.iframe){
26228             return;
26229         }
26230         if(typeof w == 'number'){
26231             
26232             this.iframe.style.width = w + 'px';
26233         }
26234         if(typeof h == 'number'){
26235             
26236             this.iframe.style.height = h + 'px';
26237             if(this.doc){
26238                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26239             }
26240         }
26241         
26242     },
26243
26244     /**
26245      * Toggles the editor between standard and source edit mode.
26246      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26247      */
26248     toggleSourceEdit : function(sourceEditMode){
26249         
26250         this.sourceEditMode = sourceEditMode === true;
26251         
26252         if(this.sourceEditMode){
26253  
26254             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26255             
26256         }else{
26257             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26258             //this.iframe.className = '';
26259             this.deferFocus();
26260         }
26261         //this.setSize(this.owner.wrap.getSize());
26262         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26263     },
26264
26265     
26266   
26267
26268     /**
26269      * Protected method that will not generally be called directly. If you need/want
26270      * custom HTML cleanup, this is the method you should override.
26271      * @param {String} html The HTML to be cleaned
26272      * return {String} The cleaned HTML
26273      */
26274     cleanHtml : function(html){
26275         html = String(html);
26276         if(html.length > 5){
26277             if(Roo.isSafari){ // strip safari nonsense
26278                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26279             }
26280         }
26281         if(html == '&nbsp;'){
26282             html = '';
26283         }
26284         return html;
26285     },
26286
26287     /**
26288      * HTML Editor -> Textarea
26289      * Protected method that will not generally be called directly. Syncs the contents
26290      * of the editor iframe with the textarea.
26291      */
26292     syncValue : function(){
26293         if(this.initialized){
26294             var bd = (this.doc.body || this.doc.documentElement);
26295             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26296             var html = bd.innerHTML;
26297             if(Roo.isSafari){
26298                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26299                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26300                 if(m && m[1]){
26301                     html = '<div style="'+m[0]+'">' + html + '</div>';
26302                 }
26303             }
26304             html = this.cleanHtml(html);
26305             // fix up the special chars.. normaly like back quotes in word...
26306             // however we do not want to do this with chinese..
26307             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26308                 
26309                 var cc = match.charCodeAt();
26310
26311                 // Get the character value, handling surrogate pairs
26312                 if (match.length == 2) {
26313                     // It's a surrogate pair, calculate the Unicode code point
26314                     var high = match.charCodeAt(0) - 0xD800;
26315                     var low  = match.charCodeAt(1) - 0xDC00;
26316                     cc = (high * 0x400) + low + 0x10000;
26317                 }  else if (
26318                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26319                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26320                     (cc >= 0xf900 && cc < 0xfb00 )
26321                 ) {
26322                         return match;
26323                 }  
26324          
26325                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26326                 return "&#" + cc + ";";
26327                 
26328                 
26329             });
26330             
26331             
26332              
26333             if(this.owner.fireEvent('beforesync', this, html) !== false){
26334                 this.el.dom.value = html;
26335                 this.owner.fireEvent('sync', this, html);
26336             }
26337         }
26338     },
26339
26340     /**
26341      * Protected method that will not generally be called directly. Pushes the value of the textarea
26342      * into the iframe editor.
26343      */
26344     pushValue : function(){
26345         if(this.initialized){
26346             var v = this.el.dom.value.trim();
26347             
26348 //            if(v.length < 1){
26349 //                v = '&#160;';
26350 //            }
26351             
26352             if(this.owner.fireEvent('beforepush', this, v) !== false){
26353                 var d = (this.doc.body || this.doc.documentElement);
26354                 d.innerHTML = v;
26355                 this.cleanUpPaste();
26356                 this.el.dom.value = d.innerHTML;
26357                 this.owner.fireEvent('push', this, v);
26358             }
26359         }
26360     },
26361
26362     // private
26363     deferFocus : function(){
26364         this.focus.defer(10, this);
26365     },
26366
26367     // doc'ed in Field
26368     focus : function(){
26369         if(this.win && !this.sourceEditMode){
26370             this.win.focus();
26371         }else{
26372             this.el.focus();
26373         }
26374     },
26375     
26376     assignDocWin: function()
26377     {
26378         var iframe = this.iframe;
26379         
26380          if(Roo.isIE){
26381             this.doc = iframe.contentWindow.document;
26382             this.win = iframe.contentWindow;
26383         } else {
26384 //            if (!Roo.get(this.frameId)) {
26385 //                return;
26386 //            }
26387 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26388 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26389             
26390             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26391                 return;
26392             }
26393             
26394             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26395             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26396         }
26397     },
26398     
26399     // private
26400     initEditor : function(){
26401         //console.log("INIT EDITOR");
26402         this.assignDocWin();
26403         
26404         
26405         
26406         this.doc.designMode="on";
26407         this.doc.open();
26408         this.doc.write(this.getDocMarkup());
26409         this.doc.close();
26410         
26411         var dbody = (this.doc.body || this.doc.documentElement);
26412         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26413         // this copies styles from the containing element into thsi one..
26414         // not sure why we need all of this..
26415         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26416         
26417         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26418         //ss['background-attachment'] = 'fixed'; // w3c
26419         dbody.bgProperties = 'fixed'; // ie
26420         //Roo.DomHelper.applyStyles(dbody, ss);
26421         Roo.EventManager.on(this.doc, {
26422             //'mousedown': this.onEditorEvent,
26423             'mouseup': this.onEditorEvent,
26424             'dblclick': this.onEditorEvent,
26425             'click': this.onEditorEvent,
26426             'keyup': this.onEditorEvent,
26427             buffer:100,
26428             scope: this
26429         });
26430         if(Roo.isGecko){
26431             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26432         }
26433         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26434             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26435         }
26436         this.initialized = true;
26437
26438         this.owner.fireEvent('initialize', this);
26439         this.pushValue();
26440     },
26441
26442     // private
26443     onDestroy : function(){
26444         
26445         
26446         
26447         if(this.rendered){
26448             
26449             //for (var i =0; i < this.toolbars.length;i++) {
26450             //    // fixme - ask toolbars for heights?
26451             //    this.toolbars[i].onDestroy();
26452            // }
26453             
26454             //this.wrap.dom.innerHTML = '';
26455             //this.wrap.remove();
26456         }
26457     },
26458
26459     // private
26460     onFirstFocus : function(){
26461         
26462         this.assignDocWin();
26463         
26464         
26465         this.activated = true;
26466          
26467     
26468         if(Roo.isGecko){ // prevent silly gecko errors
26469             this.win.focus();
26470             var s = this.win.getSelection();
26471             if(!s.focusNode || s.focusNode.nodeType != 3){
26472                 var r = s.getRangeAt(0);
26473                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26474                 r.collapse(true);
26475                 this.deferFocus();
26476             }
26477             try{
26478                 this.execCmd('useCSS', true);
26479                 this.execCmd('styleWithCSS', false);
26480             }catch(e){}
26481         }
26482         this.owner.fireEvent('activate', this);
26483     },
26484
26485     // private
26486     adjustFont: function(btn){
26487         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26488         //if(Roo.isSafari){ // safari
26489         //    adjust *= 2;
26490        // }
26491         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26492         if(Roo.isSafari){ // safari
26493             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26494             v =  (v < 10) ? 10 : v;
26495             v =  (v > 48) ? 48 : v;
26496             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26497             
26498         }
26499         
26500         
26501         v = Math.max(1, v+adjust);
26502         
26503         this.execCmd('FontSize', v  );
26504     },
26505
26506     onEditorEvent : function(e)
26507     {
26508         this.owner.fireEvent('editorevent', this, e);
26509       //  this.updateToolbar();
26510         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26511     },
26512
26513     insertTag : function(tg)
26514     {
26515         // could be a bit smarter... -> wrap the current selected tRoo..
26516         if (tg.toLowerCase() == 'span' ||
26517             tg.toLowerCase() == 'code' ||
26518             tg.toLowerCase() == 'sup' ||
26519             tg.toLowerCase() == 'sub' 
26520             ) {
26521             
26522             range = this.createRange(this.getSelection());
26523             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26524             wrappingNode.appendChild(range.extractContents());
26525             range.insertNode(wrappingNode);
26526
26527             return;
26528             
26529             
26530             
26531         }
26532         this.execCmd("formatblock",   tg);
26533         
26534     },
26535     
26536     insertText : function(txt)
26537     {
26538         
26539         
26540         var range = this.createRange();
26541         range.deleteContents();
26542                //alert(Sender.getAttribute('label'));
26543                
26544         range.insertNode(this.doc.createTextNode(txt));
26545     } ,
26546     
26547      
26548
26549     /**
26550      * Executes a Midas editor command on the editor document and performs necessary focus and
26551      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26552      * @param {String} cmd The Midas command
26553      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26554      */
26555     relayCmd : function(cmd, value){
26556         this.win.focus();
26557         this.execCmd(cmd, value);
26558         this.owner.fireEvent('editorevent', this);
26559         //this.updateToolbar();
26560         this.owner.deferFocus();
26561     },
26562
26563     /**
26564      * Executes a Midas editor command directly on the editor document.
26565      * For visual commands, you should use {@link #relayCmd} instead.
26566      * <b>This should only be called after the editor is initialized.</b>
26567      * @param {String} cmd The Midas command
26568      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26569      */
26570     execCmd : function(cmd, value){
26571         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26572         this.syncValue();
26573     },
26574  
26575  
26576    
26577     /**
26578      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26579      * to insert tRoo.
26580      * @param {String} text | dom node.. 
26581      */
26582     insertAtCursor : function(text)
26583     {
26584         
26585         if(!this.activated){
26586             return;
26587         }
26588         /*
26589         if(Roo.isIE){
26590             this.win.focus();
26591             var r = this.doc.selection.createRange();
26592             if(r){
26593                 r.collapse(true);
26594                 r.pasteHTML(text);
26595                 this.syncValue();
26596                 this.deferFocus();
26597             
26598             }
26599             return;
26600         }
26601         */
26602         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26603             this.win.focus();
26604             
26605             
26606             // from jquery ui (MIT licenced)
26607             var range, node;
26608             var win = this.win;
26609             
26610             if (win.getSelection && win.getSelection().getRangeAt) {
26611                 range = win.getSelection().getRangeAt(0);
26612                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26613                 range.insertNode(node);
26614             } else if (win.document.selection && win.document.selection.createRange) {
26615                 // no firefox support
26616                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26617                 win.document.selection.createRange().pasteHTML(txt);
26618             } else {
26619                 // no firefox support
26620                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26621                 this.execCmd('InsertHTML', txt);
26622             } 
26623             
26624             this.syncValue();
26625             
26626             this.deferFocus();
26627         }
26628     },
26629  // private
26630     mozKeyPress : function(e){
26631         if(e.ctrlKey){
26632             var c = e.getCharCode(), cmd;
26633           
26634             if(c > 0){
26635                 c = String.fromCharCode(c).toLowerCase();
26636                 switch(c){
26637                     case 'b':
26638                         cmd = 'bold';
26639                         break;
26640                     case 'i':
26641                         cmd = 'italic';
26642                         break;
26643                     
26644                     case 'u':
26645                         cmd = 'underline';
26646                         break;
26647                     
26648                     case 'v':
26649                         this.cleanUpPaste.defer(100, this);
26650                         return;
26651                         
26652                 }
26653                 if(cmd){
26654                     this.win.focus();
26655                     this.execCmd(cmd);
26656                     this.deferFocus();
26657                     e.preventDefault();
26658                 }
26659                 
26660             }
26661         }
26662     },
26663
26664     // private
26665     fixKeys : function(){ // load time branching for fastest keydown performance
26666         if(Roo.isIE){
26667             return function(e){
26668                 var k = e.getKey(), r;
26669                 if(k == e.TAB){
26670                     e.stopEvent();
26671                     r = this.doc.selection.createRange();
26672                     if(r){
26673                         r.collapse(true);
26674                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26675                         this.deferFocus();
26676                     }
26677                     return;
26678                 }
26679                 
26680                 if(k == e.ENTER){
26681                     r = this.doc.selection.createRange();
26682                     if(r){
26683                         var target = r.parentElement();
26684                         if(!target || target.tagName.toLowerCase() != 'li'){
26685                             e.stopEvent();
26686                             r.pasteHTML('<br />');
26687                             r.collapse(false);
26688                             r.select();
26689                         }
26690                     }
26691                 }
26692                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26693                     this.cleanUpPaste.defer(100, this);
26694                     return;
26695                 }
26696                 
26697                 
26698             };
26699         }else if(Roo.isOpera){
26700             return function(e){
26701                 var k = e.getKey();
26702                 if(k == e.TAB){
26703                     e.stopEvent();
26704                     this.win.focus();
26705                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26706                     this.deferFocus();
26707                 }
26708                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26709                     this.cleanUpPaste.defer(100, this);
26710                     return;
26711                 }
26712                 
26713             };
26714         }else if(Roo.isSafari){
26715             return function(e){
26716                 var k = e.getKey();
26717                 
26718                 if(k == e.TAB){
26719                     e.stopEvent();
26720                     this.execCmd('InsertText','\t');
26721                     this.deferFocus();
26722                     return;
26723                 }
26724                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26725                     this.cleanUpPaste.defer(100, this);
26726                     return;
26727                 }
26728                 
26729              };
26730         }
26731     }(),
26732     
26733     getAllAncestors: function()
26734     {
26735         var p = this.getSelectedNode();
26736         var a = [];
26737         if (!p) {
26738             a.push(p); // push blank onto stack..
26739             p = this.getParentElement();
26740         }
26741         
26742         
26743         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26744             a.push(p);
26745             p = p.parentNode;
26746         }
26747         a.push(this.doc.body);
26748         return a;
26749     },
26750     lastSel : false,
26751     lastSelNode : false,
26752     
26753     
26754     getSelection : function() 
26755     {
26756         this.assignDocWin();
26757         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26758     },
26759     
26760     getSelectedNode: function() 
26761     {
26762         // this may only work on Gecko!!!
26763         
26764         // should we cache this!!!!
26765         
26766         
26767         
26768          
26769         var range = this.createRange(this.getSelection()).cloneRange();
26770         
26771         if (Roo.isIE) {
26772             var parent = range.parentElement();
26773             while (true) {
26774                 var testRange = range.duplicate();
26775                 testRange.moveToElementText(parent);
26776                 if (testRange.inRange(range)) {
26777                     break;
26778                 }
26779                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26780                     break;
26781                 }
26782                 parent = parent.parentElement;
26783             }
26784             return parent;
26785         }
26786         
26787         // is ancestor a text element.
26788         var ac =  range.commonAncestorContainer;
26789         if (ac.nodeType == 3) {
26790             ac = ac.parentNode;
26791         }
26792         
26793         var ar = ac.childNodes;
26794          
26795         var nodes = [];
26796         var other_nodes = [];
26797         var has_other_nodes = false;
26798         for (var i=0;i<ar.length;i++) {
26799             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26800                 continue;
26801             }
26802             // fullly contained node.
26803             
26804             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26805                 nodes.push(ar[i]);
26806                 continue;
26807             }
26808             
26809             // probably selected..
26810             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26811                 other_nodes.push(ar[i]);
26812                 continue;
26813             }
26814             // outer..
26815             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26816                 continue;
26817             }
26818             
26819             
26820             has_other_nodes = true;
26821         }
26822         if (!nodes.length && other_nodes.length) {
26823             nodes= other_nodes;
26824         }
26825         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26826             return false;
26827         }
26828         
26829         return nodes[0];
26830     },
26831     createRange: function(sel)
26832     {
26833         // this has strange effects when using with 
26834         // top toolbar - not sure if it's a great idea.
26835         //this.editor.contentWindow.focus();
26836         if (typeof sel != "undefined") {
26837             try {
26838                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26839             } catch(e) {
26840                 return this.doc.createRange();
26841             }
26842         } else {
26843             return this.doc.createRange();
26844         }
26845     },
26846     getParentElement: function()
26847     {
26848         
26849         this.assignDocWin();
26850         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26851         
26852         var range = this.createRange(sel);
26853          
26854         try {
26855             var p = range.commonAncestorContainer;
26856             while (p.nodeType == 3) { // text node
26857                 p = p.parentNode;
26858             }
26859             return p;
26860         } catch (e) {
26861             return null;
26862         }
26863     
26864     },
26865     /***
26866      *
26867      * Range intersection.. the hard stuff...
26868      *  '-1' = before
26869      *  '0' = hits..
26870      *  '1' = after.
26871      *         [ -- selected range --- ]
26872      *   [fail]                        [fail]
26873      *
26874      *    basically..
26875      *      if end is before start or  hits it. fail.
26876      *      if start is after end or hits it fail.
26877      *
26878      *   if either hits (but other is outside. - then it's not 
26879      *   
26880      *    
26881      **/
26882     
26883     
26884     // @see http://www.thismuchiknow.co.uk/?p=64.
26885     rangeIntersectsNode : function(range, node)
26886     {
26887         var nodeRange = node.ownerDocument.createRange();
26888         try {
26889             nodeRange.selectNode(node);
26890         } catch (e) {
26891             nodeRange.selectNodeContents(node);
26892         }
26893     
26894         var rangeStartRange = range.cloneRange();
26895         rangeStartRange.collapse(true);
26896     
26897         var rangeEndRange = range.cloneRange();
26898         rangeEndRange.collapse(false);
26899     
26900         var nodeStartRange = nodeRange.cloneRange();
26901         nodeStartRange.collapse(true);
26902     
26903         var nodeEndRange = nodeRange.cloneRange();
26904         nodeEndRange.collapse(false);
26905     
26906         return rangeStartRange.compareBoundaryPoints(
26907                  Range.START_TO_START, nodeEndRange) == -1 &&
26908                rangeEndRange.compareBoundaryPoints(
26909                  Range.START_TO_START, nodeStartRange) == 1;
26910         
26911          
26912     },
26913     rangeCompareNode : function(range, node)
26914     {
26915         var nodeRange = node.ownerDocument.createRange();
26916         try {
26917             nodeRange.selectNode(node);
26918         } catch (e) {
26919             nodeRange.selectNodeContents(node);
26920         }
26921         
26922         
26923         range.collapse(true);
26924     
26925         nodeRange.collapse(true);
26926      
26927         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26928         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26929          
26930         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26931         
26932         var nodeIsBefore   =  ss == 1;
26933         var nodeIsAfter    = ee == -1;
26934         
26935         if (nodeIsBefore && nodeIsAfter) {
26936             return 0; // outer
26937         }
26938         if (!nodeIsBefore && nodeIsAfter) {
26939             return 1; //right trailed.
26940         }
26941         
26942         if (nodeIsBefore && !nodeIsAfter) {
26943             return 2;  // left trailed.
26944         }
26945         // fully contined.
26946         return 3;
26947     },
26948
26949     // private? - in a new class?
26950     cleanUpPaste :  function()
26951     {
26952         // cleans up the whole document..
26953         Roo.log('cleanuppaste');
26954         
26955         this.cleanUpChildren(this.doc.body);
26956         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26957         if (clean != this.doc.body.innerHTML) {
26958             this.doc.body.innerHTML = clean;
26959         }
26960         
26961     },
26962     
26963     cleanWordChars : function(input) {// change the chars to hex code
26964         var he = Roo.HtmlEditorCore;
26965         
26966         var output = input;
26967         Roo.each(he.swapCodes, function(sw) { 
26968             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26969             
26970             output = output.replace(swapper, sw[1]);
26971         });
26972         
26973         return output;
26974     },
26975     
26976     
26977     cleanUpChildren : function (n)
26978     {
26979         if (!n.childNodes.length) {
26980             return;
26981         }
26982         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26983            this.cleanUpChild(n.childNodes[i]);
26984         }
26985     },
26986     
26987     
26988         
26989     
26990     cleanUpChild : function (node)
26991     {
26992         var ed = this;
26993         //console.log(node);
26994         if (node.nodeName == "#text") {
26995             // clean up silly Windows -- stuff?
26996             return; 
26997         }
26998         if (node.nodeName == "#comment") {
26999             if (!this.allowComments) {
27000                 node.parentNode.removeChild(node);
27001             }
27002             // clean up silly Windows -- stuff?
27003             return; 
27004         }
27005         var lcname = node.tagName.toLowerCase();
27006         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27007         // whitelist of tags..
27008         
27009         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27010             // remove node.
27011             node.parentNode.removeChild(node);
27012             return;
27013             
27014         }
27015         
27016         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27017         
27018         // spans with no attributes - just remove them..
27019         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27020             remove_keep_children = true;
27021         }
27022         
27023         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27024         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27025         
27026         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27027         //    remove_keep_children = true;
27028         //}
27029         
27030         if (remove_keep_children) {
27031             this.cleanUpChildren(node);
27032             // inserts everything just before this node...
27033             while (node.childNodes.length) {
27034                 var cn = node.childNodes[0];
27035                 node.removeChild(cn);
27036                 node.parentNode.insertBefore(cn, node);
27037             }
27038             node.parentNode.removeChild(node);
27039             return;
27040         }
27041         
27042         if (!node.attributes || !node.attributes.length) {
27043             
27044           
27045             
27046             
27047             this.cleanUpChildren(node);
27048             return;
27049         }
27050         
27051         function cleanAttr(n,v)
27052         {
27053             
27054             if (v.match(/^\./) || v.match(/^\//)) {
27055                 return;
27056             }
27057             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27058                 return;
27059             }
27060             if (v.match(/^#/)) {
27061                 return;
27062             }
27063             if (v.match(/^\{/)) { // allow template editing.
27064                 return;
27065             }
27066 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27067             node.removeAttribute(n);
27068             
27069         }
27070         
27071         var cwhite = this.cwhite;
27072         var cblack = this.cblack;
27073             
27074         function cleanStyle(n,v)
27075         {
27076             if (v.match(/expression/)) { //XSS?? should we even bother..
27077                 node.removeAttribute(n);
27078                 return;
27079             }
27080             
27081             var parts = v.split(/;/);
27082             var clean = [];
27083             
27084             Roo.each(parts, function(p) {
27085                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27086                 if (!p.length) {
27087                     return true;
27088                 }
27089                 var l = p.split(':').shift().replace(/\s+/g,'');
27090                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27091                 
27092                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27093 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27094                     //node.removeAttribute(n);
27095                     return true;
27096                 }
27097                 //Roo.log()
27098                 // only allow 'c whitelisted system attributes'
27099                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27100 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27101                     //node.removeAttribute(n);
27102                     return true;
27103                 }
27104                 
27105                 
27106                  
27107                 
27108                 clean.push(p);
27109                 return true;
27110             });
27111             if (clean.length) { 
27112                 node.setAttribute(n, clean.join(';'));
27113             } else {
27114                 node.removeAttribute(n);
27115             }
27116             
27117         }
27118         
27119         
27120         for (var i = node.attributes.length-1; i > -1 ; i--) {
27121             var a = node.attributes[i];
27122             //console.log(a);
27123             
27124             if (a.name.toLowerCase().substr(0,2)=='on')  {
27125                 node.removeAttribute(a.name);
27126                 continue;
27127             }
27128             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27129                 node.removeAttribute(a.name);
27130                 continue;
27131             }
27132             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27133                 cleanAttr(a.name,a.value); // fixme..
27134                 continue;
27135             }
27136             if (a.name == 'style') {
27137                 cleanStyle(a.name,a.value);
27138                 continue;
27139             }
27140             /// clean up MS crap..
27141             // tecnically this should be a list of valid class'es..
27142             
27143             
27144             if (a.name == 'class') {
27145                 if (a.value.match(/^Mso/)) {
27146                     node.removeAttribute('class');
27147                 }
27148                 
27149                 if (a.value.match(/^body$/)) {
27150                     node.removeAttribute('class');
27151                 }
27152                 continue;
27153             }
27154             
27155             // style cleanup!?
27156             // class cleanup?
27157             
27158         }
27159         
27160         
27161         this.cleanUpChildren(node);
27162         
27163         
27164     },
27165     
27166     /**
27167      * Clean up MS wordisms...
27168      */
27169     cleanWord : function(node)
27170     {
27171         if (!node) {
27172             this.cleanWord(this.doc.body);
27173             return;
27174         }
27175         
27176         if(
27177                 node.nodeName == 'SPAN' &&
27178                 !node.hasAttributes() &&
27179                 node.childNodes.length == 1 &&
27180                 node.firstChild.nodeName == "#text"  
27181         ) {
27182             var textNode = node.firstChild;
27183             node.removeChild(textNode);
27184             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27185                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27186             }
27187             node.parentNode.insertBefore(textNode, node);
27188             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27189                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27190             }
27191             node.parentNode.removeChild(node);
27192         }
27193         
27194         if (node.nodeName == "#text") {
27195             // clean up silly Windows -- stuff?
27196             return; 
27197         }
27198         if (node.nodeName == "#comment") {
27199             node.parentNode.removeChild(node);
27200             // clean up silly Windows -- stuff?
27201             return; 
27202         }
27203         
27204         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27205             node.parentNode.removeChild(node);
27206             return;
27207         }
27208         //Roo.log(node.tagName);
27209         // remove - but keep children..
27210         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27211             //Roo.log('-- removed');
27212             while (node.childNodes.length) {
27213                 var cn = node.childNodes[0];
27214                 node.removeChild(cn);
27215                 node.parentNode.insertBefore(cn, node);
27216                 // move node to parent - and clean it..
27217                 this.cleanWord(cn);
27218             }
27219             node.parentNode.removeChild(node);
27220             /// no need to iterate chidlren = it's got none..
27221             //this.iterateChildren(node, this.cleanWord);
27222             return;
27223         }
27224         // clean styles
27225         if (node.className.length) {
27226             
27227             var cn = node.className.split(/\W+/);
27228             var cna = [];
27229             Roo.each(cn, function(cls) {
27230                 if (cls.match(/Mso[a-zA-Z]+/)) {
27231                     return;
27232                 }
27233                 cna.push(cls);
27234             });
27235             node.className = cna.length ? cna.join(' ') : '';
27236             if (!cna.length) {
27237                 node.removeAttribute("class");
27238             }
27239         }
27240         
27241         if (node.hasAttribute("lang")) {
27242             node.removeAttribute("lang");
27243         }
27244         
27245         if (node.hasAttribute("style")) {
27246             
27247             var styles = node.getAttribute("style").split(";");
27248             var nstyle = [];
27249             Roo.each(styles, function(s) {
27250                 if (!s.match(/:/)) {
27251                     return;
27252                 }
27253                 var kv = s.split(":");
27254                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27255                     return;
27256                 }
27257                 // what ever is left... we allow.
27258                 nstyle.push(s);
27259             });
27260             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27261             if (!nstyle.length) {
27262                 node.removeAttribute('style');
27263             }
27264         }
27265         this.iterateChildren(node, this.cleanWord);
27266         
27267         
27268         
27269     },
27270     /**
27271      * iterateChildren of a Node, calling fn each time, using this as the scole..
27272      * @param {DomNode} node node to iterate children of.
27273      * @param {Function} fn method of this class to call on each item.
27274      */
27275     iterateChildren : function(node, fn)
27276     {
27277         if (!node.childNodes.length) {
27278                 return;
27279         }
27280         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27281            fn.call(this, node.childNodes[i])
27282         }
27283     },
27284     
27285     
27286     /**
27287      * cleanTableWidths.
27288      *
27289      * Quite often pasting from word etc.. results in tables with column and widths.
27290      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27291      *
27292      */
27293     cleanTableWidths : function(node)
27294     {
27295          
27296          
27297         if (!node) {
27298             this.cleanTableWidths(this.doc.body);
27299             return;
27300         }
27301         
27302         // ignore list...
27303         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27304             return; 
27305         }
27306         Roo.log(node.tagName);
27307         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27308             this.iterateChildren(node, this.cleanTableWidths);
27309             return;
27310         }
27311         if (node.hasAttribute('width')) {
27312             node.removeAttribute('width');
27313         }
27314         
27315          
27316         if (node.hasAttribute("style")) {
27317             // pretty basic...
27318             
27319             var styles = node.getAttribute("style").split(";");
27320             var nstyle = [];
27321             Roo.each(styles, function(s) {
27322                 if (!s.match(/:/)) {
27323                     return;
27324                 }
27325                 var kv = s.split(":");
27326                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27327                     return;
27328                 }
27329                 // what ever is left... we allow.
27330                 nstyle.push(s);
27331             });
27332             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27333             if (!nstyle.length) {
27334                 node.removeAttribute('style');
27335             }
27336         }
27337         
27338         this.iterateChildren(node, this.cleanTableWidths);
27339         
27340         
27341     },
27342     
27343     
27344     
27345     
27346     domToHTML : function(currentElement, depth, nopadtext) {
27347         
27348         depth = depth || 0;
27349         nopadtext = nopadtext || false;
27350     
27351         if (!currentElement) {
27352             return this.domToHTML(this.doc.body);
27353         }
27354         
27355         //Roo.log(currentElement);
27356         var j;
27357         var allText = false;
27358         var nodeName = currentElement.nodeName;
27359         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27360         
27361         if  (nodeName == '#text') {
27362             
27363             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27364         }
27365         
27366         
27367         var ret = '';
27368         if (nodeName != 'BODY') {
27369              
27370             var i = 0;
27371             // Prints the node tagName, such as <A>, <IMG>, etc
27372             if (tagName) {
27373                 var attr = [];
27374                 for(i = 0; i < currentElement.attributes.length;i++) {
27375                     // quoting?
27376                     var aname = currentElement.attributes.item(i).name;
27377                     if (!currentElement.attributes.item(i).value.length) {
27378                         continue;
27379                     }
27380                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27381                 }
27382                 
27383                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27384             } 
27385             else {
27386                 
27387                 // eack
27388             }
27389         } else {
27390             tagName = false;
27391         }
27392         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27393             return ret;
27394         }
27395         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27396             nopadtext = true;
27397         }
27398         
27399         
27400         // Traverse the tree
27401         i = 0;
27402         var currentElementChild = currentElement.childNodes.item(i);
27403         var allText = true;
27404         var innerHTML  = '';
27405         lastnode = '';
27406         while (currentElementChild) {
27407             // Formatting code (indent the tree so it looks nice on the screen)
27408             var nopad = nopadtext;
27409             if (lastnode == 'SPAN') {
27410                 nopad  = true;
27411             }
27412             // text
27413             if  (currentElementChild.nodeName == '#text') {
27414                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27415                 toadd = nopadtext ? toadd : toadd.trim();
27416                 if (!nopad && toadd.length > 80) {
27417                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27418                 }
27419                 innerHTML  += toadd;
27420                 
27421                 i++;
27422                 currentElementChild = currentElement.childNodes.item(i);
27423                 lastNode = '';
27424                 continue;
27425             }
27426             allText = false;
27427             
27428             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27429                 
27430             // Recursively traverse the tree structure of the child node
27431             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27432             lastnode = currentElementChild.nodeName;
27433             i++;
27434             currentElementChild=currentElement.childNodes.item(i);
27435         }
27436         
27437         ret += innerHTML;
27438         
27439         if (!allText) {
27440                 // The remaining code is mostly for formatting the tree
27441             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27442         }
27443         
27444         
27445         if (tagName) {
27446             ret+= "</"+tagName+">";
27447         }
27448         return ret;
27449         
27450     },
27451         
27452     applyBlacklists : function()
27453     {
27454         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27455         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27456         
27457         this.white = [];
27458         this.black = [];
27459         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27460             if (b.indexOf(tag) > -1) {
27461                 return;
27462             }
27463             this.white.push(tag);
27464             
27465         }, this);
27466         
27467         Roo.each(w, function(tag) {
27468             if (b.indexOf(tag) > -1) {
27469                 return;
27470             }
27471             if (this.white.indexOf(tag) > -1) {
27472                 return;
27473             }
27474             this.white.push(tag);
27475             
27476         }, this);
27477         
27478         
27479         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27480             if (w.indexOf(tag) > -1) {
27481                 return;
27482             }
27483             this.black.push(tag);
27484             
27485         }, this);
27486         
27487         Roo.each(b, function(tag) {
27488             if (w.indexOf(tag) > -1) {
27489                 return;
27490             }
27491             if (this.black.indexOf(tag) > -1) {
27492                 return;
27493             }
27494             this.black.push(tag);
27495             
27496         }, this);
27497         
27498         
27499         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27500         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27501         
27502         this.cwhite = [];
27503         this.cblack = [];
27504         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27505             if (b.indexOf(tag) > -1) {
27506                 return;
27507             }
27508             this.cwhite.push(tag);
27509             
27510         }, this);
27511         
27512         Roo.each(w, function(tag) {
27513             if (b.indexOf(tag) > -1) {
27514                 return;
27515             }
27516             if (this.cwhite.indexOf(tag) > -1) {
27517                 return;
27518             }
27519             this.cwhite.push(tag);
27520             
27521         }, this);
27522         
27523         
27524         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27525             if (w.indexOf(tag) > -1) {
27526                 return;
27527             }
27528             this.cblack.push(tag);
27529             
27530         }, this);
27531         
27532         Roo.each(b, function(tag) {
27533             if (w.indexOf(tag) > -1) {
27534                 return;
27535             }
27536             if (this.cblack.indexOf(tag) > -1) {
27537                 return;
27538             }
27539             this.cblack.push(tag);
27540             
27541         }, this);
27542     },
27543     
27544     setStylesheets : function(stylesheets)
27545     {
27546         if(typeof(stylesheets) == 'string'){
27547             Roo.get(this.iframe.contentDocument.head).createChild({
27548                 tag : 'link',
27549                 rel : 'stylesheet',
27550                 type : 'text/css',
27551                 href : stylesheets
27552             });
27553             
27554             return;
27555         }
27556         var _this = this;
27557      
27558         Roo.each(stylesheets, function(s) {
27559             if(!s.length){
27560                 return;
27561             }
27562             
27563             Roo.get(_this.iframe.contentDocument.head).createChild({
27564                 tag : 'link',
27565                 rel : 'stylesheet',
27566                 type : 'text/css',
27567                 href : s
27568             });
27569         });
27570
27571         
27572     },
27573     
27574     removeStylesheets : function()
27575     {
27576         var _this = this;
27577         
27578         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27579             s.remove();
27580         });
27581     },
27582     
27583     setStyle : function(style)
27584     {
27585         Roo.get(this.iframe.contentDocument.head).createChild({
27586             tag : 'style',
27587             type : 'text/css',
27588             html : style
27589         });
27590
27591         return;
27592     }
27593     
27594     // hide stuff that is not compatible
27595     /**
27596      * @event blur
27597      * @hide
27598      */
27599     /**
27600      * @event change
27601      * @hide
27602      */
27603     /**
27604      * @event focus
27605      * @hide
27606      */
27607     /**
27608      * @event specialkey
27609      * @hide
27610      */
27611     /**
27612      * @cfg {String} fieldClass @hide
27613      */
27614     /**
27615      * @cfg {String} focusClass @hide
27616      */
27617     /**
27618      * @cfg {String} autoCreate @hide
27619      */
27620     /**
27621      * @cfg {String} inputType @hide
27622      */
27623     /**
27624      * @cfg {String} invalidClass @hide
27625      */
27626     /**
27627      * @cfg {String} invalidText @hide
27628      */
27629     /**
27630      * @cfg {String} msgFx @hide
27631      */
27632     /**
27633      * @cfg {String} validateOnBlur @hide
27634      */
27635 });
27636
27637 Roo.HtmlEditorCore.white = [
27638         'area', 'br', 'img', 'input', 'hr', 'wbr',
27639         
27640        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27641        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27642        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27643        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27644        'table',   'ul',         'xmp', 
27645        
27646        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27647       'thead',   'tr', 
27648      
27649       'dir', 'menu', 'ol', 'ul', 'dl',
27650        
27651       'embed',  'object'
27652 ];
27653
27654
27655 Roo.HtmlEditorCore.black = [
27656     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27657         'applet', // 
27658         'base',   'basefont', 'bgsound', 'blink',  'body', 
27659         'frame',  'frameset', 'head',    'html',   'ilayer', 
27660         'iframe', 'layer',  'link',     'meta',    'object',   
27661         'script', 'style' ,'title',  'xml' // clean later..
27662 ];
27663 Roo.HtmlEditorCore.clean = [
27664     'script', 'style', 'title', 'xml'
27665 ];
27666 Roo.HtmlEditorCore.remove = [
27667     'font'
27668 ];
27669 // attributes..
27670
27671 Roo.HtmlEditorCore.ablack = [
27672     'on'
27673 ];
27674     
27675 Roo.HtmlEditorCore.aclean = [ 
27676     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27677 ];
27678
27679 // protocols..
27680 Roo.HtmlEditorCore.pwhite= [
27681         'http',  'https',  'mailto'
27682 ];
27683
27684 // white listed style attributes.
27685 Roo.HtmlEditorCore.cwhite= [
27686       //  'text-align', /// default is to allow most things..
27687       
27688          
27689 //        'font-size'//??
27690 ];
27691
27692 // black listed style attributes.
27693 Roo.HtmlEditorCore.cblack= [
27694       //  'font-size' -- this can be set by the project 
27695 ];
27696
27697
27698 Roo.HtmlEditorCore.swapCodes   =[ 
27699     [    8211, "&#8211;" ], 
27700     [    8212, "&#8212;" ], 
27701     [    8216,  "'" ],  
27702     [    8217, "'" ],  
27703     [    8220, '"' ],  
27704     [    8221, '"' ],  
27705     [    8226, "*" ],  
27706     [    8230, "..." ]
27707 ]; 
27708
27709     /*
27710  * - LGPL
27711  *
27712  * HtmlEditor
27713  * 
27714  */
27715
27716 /**
27717  * @class Roo.bootstrap.form.HtmlEditor
27718  * @extends Roo.bootstrap.form.TextArea
27719  * Bootstrap HtmlEditor class
27720
27721  * @constructor
27722  * Create a new HtmlEditor
27723  * @param {Object} config The config object
27724  */
27725
27726 Roo.bootstrap.form.HtmlEditor = function(config){
27727     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27728     if (!this.toolbars) {
27729         this.toolbars = [];
27730     }
27731     
27732     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27733     this.addEvents({
27734             /**
27735              * @event initialize
27736              * Fires when the editor is fully initialized (including the iframe)
27737              * @param {HtmlEditor} this
27738              */
27739             initialize: true,
27740             /**
27741              * @event activate
27742              * Fires when the editor is first receives the focus. Any insertion must wait
27743              * until after this event.
27744              * @param {HtmlEditor} this
27745              */
27746             activate: true,
27747              /**
27748              * @event beforesync
27749              * Fires before the textarea is updated with content from the editor iframe. Return false
27750              * to cancel the sync.
27751              * @param {HtmlEditor} this
27752              * @param {String} html
27753              */
27754             beforesync: true,
27755              /**
27756              * @event beforepush
27757              * Fires before the iframe editor is updated with content from the textarea. Return false
27758              * to cancel the push.
27759              * @param {HtmlEditor} this
27760              * @param {String} html
27761              */
27762             beforepush: true,
27763              /**
27764              * @event sync
27765              * Fires when the textarea is updated with content from the editor iframe.
27766              * @param {HtmlEditor} this
27767              * @param {String} html
27768              */
27769             sync: true,
27770              /**
27771              * @event push
27772              * Fires when the iframe editor is updated with content from the textarea.
27773              * @param {HtmlEditor} this
27774              * @param {String} html
27775              */
27776             push: true,
27777              /**
27778              * @event editmodechange
27779              * Fires when the editor switches edit modes
27780              * @param {HtmlEditor} this
27781              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27782              */
27783             editmodechange: true,
27784             /**
27785              * @event editorevent
27786              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27787              * @param {HtmlEditor} this
27788              */
27789             editorevent: true,
27790             /**
27791              * @event firstfocus
27792              * Fires when on first focus - needed by toolbars..
27793              * @param {HtmlEditor} this
27794              */
27795             firstfocus: true,
27796             /**
27797              * @event autosave
27798              * Auto save the htmlEditor value as a file into Events
27799              * @param {HtmlEditor} this
27800              */
27801             autosave: true,
27802             /**
27803              * @event savedpreview
27804              * preview the saved version of htmlEditor
27805              * @param {HtmlEditor} this
27806              */
27807             savedpreview: true
27808         });
27809 };
27810
27811
27812 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27813     
27814     
27815       /**
27816      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27817      */
27818     toolbars : false,
27819     
27820      /**
27821     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27822     */
27823     btns : [],
27824    
27825      /**
27826      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27827      *                        Roo.resizable.
27828      */
27829     resizable : false,
27830      /**
27831      * @cfg {Number} height (in pixels)
27832      */   
27833     height: 300,
27834    /**
27835      * @cfg {Number} width (in pixels)
27836      */   
27837     width: false,
27838     
27839     /**
27840      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27841      * 
27842      */
27843     stylesheets: false,
27844     
27845     // id of frame..
27846     frameId: false,
27847     
27848     // private properties
27849     validationEvent : false,
27850     deferHeight: true,
27851     initialized : false,
27852     activated : false,
27853     
27854     onFocus : Roo.emptyFn,
27855     iframePad:3,
27856     hideMode:'offsets',
27857     
27858     tbContainer : false,
27859     
27860     bodyCls : '',
27861     
27862     toolbarContainer :function() {
27863         return this.wrap.select('.x-html-editor-tb',true).first();
27864     },
27865
27866     /**
27867      * Protected method that will not generally be called directly. It
27868      * is called when the editor creates its toolbar. Override this method if you need to
27869      * add custom toolbar buttons.
27870      * @param {HtmlEditor} editor
27871      */
27872     createToolbar : function(){
27873         Roo.log('renewing');
27874         Roo.log("create toolbars");
27875         
27876         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27877         this.toolbars[0].render(this.toolbarContainer());
27878         
27879         return;
27880         
27881 //        if (!editor.toolbars || !editor.toolbars.length) {
27882 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27883 //        }
27884 //        
27885 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27886 //            editor.toolbars[i] = Roo.factory(
27887 //                    typeof(editor.toolbars[i]) == 'string' ?
27888 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27889 //                Roo.bootstrap.form.HtmlEditor);
27890 //            editor.toolbars[i].init(editor);
27891 //        }
27892     },
27893
27894      
27895     // private
27896     onRender : function(ct, position)
27897     {
27898        // Roo.log("Call onRender: " + this.xtype);
27899         var _t = this;
27900         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27901       
27902         this.wrap = this.inputEl().wrap({
27903             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27904         });
27905         
27906         this.editorcore.onRender(ct, position);
27907          
27908         if (this.resizable) {
27909             this.resizeEl = new Roo.Resizable(this.wrap, {
27910                 pinned : true,
27911                 wrap: true,
27912                 dynamic : true,
27913                 minHeight : this.height,
27914                 height: this.height,
27915                 handles : this.resizable,
27916                 width: this.width,
27917                 listeners : {
27918                     resize : function(r, w, h) {
27919                         _t.onResize(w,h); // -something
27920                     }
27921                 }
27922             });
27923             
27924         }
27925         this.createToolbar(this);
27926        
27927         
27928         if(!this.width && this.resizable){
27929             this.setSize(this.wrap.getSize());
27930         }
27931         if (this.resizeEl) {
27932             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27933             // should trigger onReize..
27934         }
27935         
27936     },
27937
27938     // private
27939     onResize : function(w, h)
27940     {
27941         Roo.log('resize: ' +w + ',' + h );
27942         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27943         var ew = false;
27944         var eh = false;
27945         
27946         if(this.inputEl() ){
27947             if(typeof w == 'number'){
27948                 var aw = w - this.wrap.getFrameWidth('lr');
27949                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27950                 ew = aw;
27951             }
27952             if(typeof h == 'number'){
27953                  var tbh = -11;  // fixme it needs to tool bar size!
27954                 for (var i =0; i < this.toolbars.length;i++) {
27955                     // fixme - ask toolbars for heights?
27956                     tbh += this.toolbars[i].el.getHeight();
27957                     //if (this.toolbars[i].footer) {
27958                     //    tbh += this.toolbars[i].footer.el.getHeight();
27959                     //}
27960                 }
27961               
27962                 
27963                 
27964                 
27965                 
27966                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27967                 ah -= 5; // knock a few pixes off for look..
27968                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27969                 var eh = ah;
27970             }
27971         }
27972         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27973         this.editorcore.onResize(ew,eh);
27974         
27975     },
27976
27977     /**
27978      * Toggles the editor between standard and source edit mode.
27979      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27980      */
27981     toggleSourceEdit : function(sourceEditMode)
27982     {
27983         this.editorcore.toggleSourceEdit(sourceEditMode);
27984         
27985         if(this.editorcore.sourceEditMode){
27986             Roo.log('editor - showing textarea');
27987             
27988 //            Roo.log('in');
27989 //            Roo.log(this.syncValue());
27990             this.syncValue();
27991             this.inputEl().removeClass(['hide', 'x-hidden']);
27992             this.inputEl().dom.removeAttribute('tabIndex');
27993             this.inputEl().focus();
27994         }else{
27995             Roo.log('editor - hiding textarea');
27996 //            Roo.log('out')
27997 //            Roo.log(this.pushValue()); 
27998             this.pushValue();
27999             
28000             this.inputEl().addClass(['hide', 'x-hidden']);
28001             this.inputEl().dom.setAttribute('tabIndex', -1);
28002             //this.deferFocus();
28003         }
28004          
28005         if(this.resizable){
28006             this.setSize(this.wrap.getSize());
28007         }
28008         
28009         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28010     },
28011  
28012     // private (for BoxComponent)
28013     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28014
28015     // private (for BoxComponent)
28016     getResizeEl : function(){
28017         return this.wrap;
28018     },
28019
28020     // private (for BoxComponent)
28021     getPositionEl : function(){
28022         return this.wrap;
28023     },
28024
28025     // private
28026     initEvents : function(){
28027         this.originalValue = this.getValue();
28028     },
28029
28030 //    /**
28031 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28032 //     * @method
28033 //     */
28034 //    markInvalid : Roo.emptyFn,
28035 //    /**
28036 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28037 //     * @method
28038 //     */
28039 //    clearInvalid : Roo.emptyFn,
28040
28041     setValue : function(v){
28042         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28043         this.editorcore.pushValue();
28044     },
28045
28046      
28047     // private
28048     deferFocus : function(){
28049         this.focus.defer(10, this);
28050     },
28051
28052     // doc'ed in Field
28053     focus : function(){
28054         this.editorcore.focus();
28055         
28056     },
28057       
28058
28059     // private
28060     onDestroy : function(){
28061         
28062         
28063         
28064         if(this.rendered){
28065             
28066             for (var i =0; i < this.toolbars.length;i++) {
28067                 // fixme - ask toolbars for heights?
28068                 this.toolbars[i].onDestroy();
28069             }
28070             
28071             this.wrap.dom.innerHTML = '';
28072             this.wrap.remove();
28073         }
28074     },
28075
28076     // private
28077     onFirstFocus : function(){
28078         //Roo.log("onFirstFocus");
28079         this.editorcore.onFirstFocus();
28080          for (var i =0; i < this.toolbars.length;i++) {
28081             this.toolbars[i].onFirstFocus();
28082         }
28083         
28084     },
28085     
28086     // private
28087     syncValue : function()
28088     {   
28089         this.editorcore.syncValue();
28090     },
28091     
28092     pushValue : function()
28093     {   
28094         this.editorcore.pushValue();
28095     }
28096      
28097     
28098     // hide stuff that is not compatible
28099     /**
28100      * @event blur
28101      * @hide
28102      */
28103     /**
28104      * @event change
28105      * @hide
28106      */
28107     /**
28108      * @event focus
28109      * @hide
28110      */
28111     /**
28112      * @event specialkey
28113      * @hide
28114      */
28115     /**
28116      * @cfg {String} fieldClass @hide
28117      */
28118     /**
28119      * @cfg {String} focusClass @hide
28120      */
28121     /**
28122      * @cfg {String} autoCreate @hide
28123      */
28124     /**
28125      * @cfg {String} inputType @hide
28126      */
28127      
28128     /**
28129      * @cfg {String} invalidText @hide
28130      */
28131     /**
28132      * @cfg {String} msgFx @hide
28133      */
28134     /**
28135      * @cfg {String} validateOnBlur @hide
28136      */
28137 });
28138  
28139     
28140    
28141    
28142    
28143       
28144 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28145 /**
28146  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28147  * @parent Roo.bootstrap.form.HtmlEditor
28148  * @extends Roo.bootstrap.nav.Simplebar
28149  * Basic Toolbar
28150  * 
28151  * @example
28152  * Usage:
28153  *
28154  new Roo.bootstrap.form.HtmlEditor({
28155     ....
28156     toolbars : [
28157         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28158             disable : { fonts: 1 , format: 1, ..., ... , ...],
28159             btns : [ .... ]
28160         })
28161     }
28162      
28163  * 
28164  * @cfg {Object} disable List of elements to disable..
28165  * @cfg {Array} btns List of additional buttons.
28166  * 
28167  * 
28168  * NEEDS Extra CSS? 
28169  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28170  */
28171  
28172 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28173 {
28174     
28175     Roo.apply(this, config);
28176     
28177     // default disabled, based on 'good practice'..
28178     this.disable = this.disable || {};
28179     Roo.applyIf(this.disable, {
28180         fontSize : true,
28181         colors : true,
28182         specialElements : true
28183     });
28184     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28185     
28186     this.editor = config.editor;
28187     this.editorcore = config.editor.editorcore;
28188     
28189     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28190     
28191     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28192     // dont call parent... till later.
28193 }
28194 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28195      
28196     bar : true,
28197     
28198     editor : false,
28199     editorcore : false,
28200     
28201     
28202     formats : [
28203         "p" ,  
28204         "h1","h2","h3","h4","h5","h6", 
28205         "pre", "code", 
28206         "abbr", "acronym", "address", "cite", "samp", "var",
28207         'div','span'
28208     ],
28209     
28210     onRender : function(ct, position)
28211     {
28212        // Roo.log("Call onRender: " + this.xtype);
28213         
28214        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28215        Roo.log(this.el);
28216        this.el.dom.style.marginBottom = '0';
28217        var _this = this;
28218        var editorcore = this.editorcore;
28219        var editor= this.editor;
28220        
28221        var children = [];
28222        var btn = function(id,cmd , toggle, handler, html){
28223        
28224             var  event = toggle ? 'toggle' : 'click';
28225        
28226             var a = {
28227                 size : 'sm',
28228                 xtype: 'Button',
28229                 xns: Roo.bootstrap,
28230                 //glyphicon : id,
28231                 fa: id,
28232                 cmd : id || cmd,
28233                 enableToggle:toggle !== false,
28234                 html : html || '',
28235                 pressed : toggle ? false : null,
28236                 listeners : {}
28237             };
28238             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28239                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28240             };
28241             children.push(a);
28242             return a;
28243        }
28244        
28245     //    var cb_box = function...
28246         
28247         var style = {
28248                 xtype: 'Button',
28249                 size : 'sm',
28250                 xns: Roo.bootstrap,
28251                 fa : 'font',
28252                 //html : 'submit'
28253                 menu : {
28254                     xtype: 'Menu',
28255                     xns: Roo.bootstrap,
28256                     items:  []
28257                 }
28258         };
28259         Roo.each(this.formats, function(f) {
28260             style.menu.items.push({
28261                 xtype :'MenuItem',
28262                 xns: Roo.bootstrap,
28263                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28264                 tagname : f,
28265                 listeners : {
28266                     click : function()
28267                     {
28268                         editorcore.insertTag(this.tagname);
28269                         editor.focus();
28270                     }
28271                 }
28272                 
28273             });
28274         });
28275         children.push(style);   
28276         
28277         btn('bold',false,true);
28278         btn('italic',false,true);
28279         btn('align-left', 'justifyleft',true);
28280         btn('align-center', 'justifycenter',true);
28281         btn('align-right' , 'justifyright',true);
28282         btn('link', false, false, function(btn) {
28283             //Roo.log("create link?");
28284             var url = prompt(this.createLinkText, this.defaultLinkValue);
28285             if(url && url != 'http:/'+'/'){
28286                 this.editorcore.relayCmd('createlink', url);
28287             }
28288         }),
28289         btn('list','insertunorderedlist',true);
28290         btn('pencil', false,true, function(btn){
28291                 Roo.log(this);
28292                 this.toggleSourceEdit(btn.pressed);
28293         });
28294         
28295         if (this.editor.btns.length > 0) {
28296             for (var i = 0; i<this.editor.btns.length; i++) {
28297                 children.push(this.editor.btns[i]);
28298             }
28299         }
28300         
28301         /*
28302         var cog = {
28303                 xtype: 'Button',
28304                 size : 'sm',
28305                 xns: Roo.bootstrap,
28306                 glyphicon : 'cog',
28307                 //html : 'submit'
28308                 menu : {
28309                     xtype: 'Menu',
28310                     xns: Roo.bootstrap,
28311                     items:  []
28312                 }
28313         };
28314         
28315         cog.menu.items.push({
28316             xtype :'MenuItem',
28317             xns: Roo.bootstrap,
28318             html : Clean styles,
28319             tagname : f,
28320             listeners : {
28321                 click : function()
28322                 {
28323                     editorcore.insertTag(this.tagname);
28324                     editor.focus();
28325                 }
28326             }
28327             
28328         });
28329        */
28330         
28331          
28332        this.xtype = 'NavSimplebar';
28333         
28334         for(var i=0;i< children.length;i++) {
28335             
28336             this.buttons.add(this.addxtypeChild(children[i]));
28337             
28338         }
28339         
28340         editor.on('editorevent', this.updateToolbar, this);
28341     },
28342     onBtnClick : function(id)
28343     {
28344        this.editorcore.relayCmd(id);
28345        this.editorcore.focus();
28346     },
28347     
28348     /**
28349      * Protected method that will not generally be called directly. It triggers
28350      * a toolbar update by reading the markup state of the current selection in the editor.
28351      */
28352     updateToolbar: function(){
28353
28354         if(!this.editorcore.activated){
28355             this.editor.onFirstFocus(); // is this neeed?
28356             return;
28357         }
28358
28359         var btns = this.buttons; 
28360         var doc = this.editorcore.doc;
28361         btns.get('bold').setActive(doc.queryCommandState('bold'));
28362         btns.get('italic').setActive(doc.queryCommandState('italic'));
28363         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28364         
28365         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28366         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28367         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28368         
28369         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28370         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28371          /*
28372         
28373         var ans = this.editorcore.getAllAncestors();
28374         if (this.formatCombo) {
28375             
28376             
28377             var store = this.formatCombo.store;
28378             this.formatCombo.setValue("");
28379             for (var i =0; i < ans.length;i++) {
28380                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28381                     // select it..
28382                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28383                     break;
28384                 }
28385             }
28386         }
28387         
28388         
28389         
28390         // hides menus... - so this cant be on a menu...
28391         Roo.bootstrap.MenuMgr.hideAll();
28392         */
28393         Roo.bootstrap.menu.Manager.hideAll();
28394         //this.editorsyncValue();
28395     },
28396     onFirstFocus: function() {
28397         this.buttons.each(function(item){
28398            item.enable();
28399         });
28400     },
28401     toggleSourceEdit : function(sourceEditMode){
28402         
28403           
28404         if(sourceEditMode){
28405             Roo.log("disabling buttons");
28406            this.buttons.each( function(item){
28407                 if(item.cmd != 'pencil'){
28408                     item.disable();
28409                 }
28410             });
28411           
28412         }else{
28413             Roo.log("enabling buttons");
28414             if(this.editorcore.initialized){
28415                 this.buttons.each( function(item){
28416                     item.enable();
28417                 });
28418             }
28419             
28420         }
28421         Roo.log("calling toggole on editor");
28422         // tell the editor that it's been pressed..
28423         this.editor.toggleSourceEdit(sourceEditMode);
28424        
28425     }
28426 });
28427
28428
28429
28430
28431  
28432 /*
28433  * - LGPL
28434  */
28435
28436 /**
28437  * @class Roo.bootstrap.form.Markdown
28438  * @extends Roo.bootstrap.form.TextArea
28439  * Bootstrap Showdown editable area
28440  * @cfg {string} content
28441  * 
28442  * @constructor
28443  * Create a new Showdown
28444  */
28445
28446 Roo.bootstrap.form.Markdown = function(config){
28447     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28448    
28449 };
28450
28451 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28452     
28453     editing :false,
28454     
28455     initEvents : function()
28456     {
28457         
28458         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28459         this.markdownEl = this.el.createChild({
28460             cls : 'roo-markdown-area'
28461         });
28462         this.inputEl().addClass('d-none');
28463         if (this.getValue() == '') {
28464             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28465             
28466         } else {
28467             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28468         }
28469         this.markdownEl.on('click', this.toggleTextEdit, this);
28470         this.on('blur', this.toggleTextEdit, this);
28471         this.on('specialkey', this.resizeTextArea, this);
28472     },
28473     
28474     toggleTextEdit : function()
28475     {
28476         var sh = this.markdownEl.getHeight();
28477         this.inputEl().addClass('d-none');
28478         this.markdownEl.addClass('d-none');
28479         if (!this.editing) {
28480             // show editor?
28481             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28482             this.inputEl().removeClass('d-none');
28483             this.inputEl().focus();
28484             this.editing = true;
28485             return;
28486         }
28487         // show showdown...
28488         this.updateMarkdown();
28489         this.markdownEl.removeClass('d-none');
28490         this.editing = false;
28491         return;
28492     },
28493     updateMarkdown : function()
28494     {
28495         if (this.getValue() == '') {
28496             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28497             return;
28498         }
28499  
28500         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28501     },
28502     
28503     resizeTextArea: function () {
28504         
28505         var sh = 100;
28506         Roo.log([sh, this.getValue().split("\n").length * 30]);
28507         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28508     },
28509     setValue : function(val)
28510     {
28511         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28512         if (!this.editing) {
28513             this.updateMarkdown();
28514         }
28515         
28516     },
28517     focus : function()
28518     {
28519         if (!this.editing) {
28520             this.toggleTextEdit();
28521         }
28522         
28523     }
28524
28525
28526 });/*
28527  * Based on:
28528  * Ext JS Library 1.1.1
28529  * Copyright(c) 2006-2007, Ext JS, LLC.
28530  *
28531  * Originally Released Under LGPL - original licence link has changed is not relivant.
28532  *
28533  * Fork - LGPL
28534  * <script type="text/javascript">
28535  */
28536  
28537 /**
28538  * @class Roo.bootstrap.PagingToolbar
28539  * @extends Roo.bootstrap.nav.Simplebar
28540  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28541  * @constructor
28542  * Create a new PagingToolbar
28543  * @param {Object} config The config object
28544  * @param {Roo.data.Store} store
28545  */
28546 Roo.bootstrap.PagingToolbar = function(config)
28547 {
28548     // old args format still supported... - xtype is prefered..
28549         // created from xtype...
28550     
28551     this.ds = config.dataSource;
28552     
28553     if (config.store && !this.ds) {
28554         this.store= Roo.factory(config.store, Roo.data);
28555         this.ds = this.store;
28556         this.ds.xmodule = this.xmodule || false;
28557     }
28558     
28559     this.toolbarItems = [];
28560     if (config.items) {
28561         this.toolbarItems = config.items;
28562     }
28563     
28564     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28565     
28566     this.cursor = 0;
28567     
28568     if (this.ds) { 
28569         this.bind(this.ds);
28570     }
28571     
28572     if (Roo.bootstrap.version == 4) {
28573         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28574     } else {
28575         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28576     }
28577     
28578 };
28579
28580 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28581     /**
28582      * @cfg {Roo.bootstrap.Button} buttons[]
28583      * Buttons for the toolbar
28584      */
28585      /**
28586      * @cfg {Roo.data.Store} store
28587      * The underlying data store providing the paged data
28588      */
28589     /**
28590      * @cfg {String/HTMLElement/Element} container
28591      * container The id or element that will contain the toolbar
28592      */
28593     /**
28594      * @cfg {Boolean} displayInfo
28595      * True to display the displayMsg (defaults to false)
28596      */
28597     /**
28598      * @cfg {Number} pageSize
28599      * The number of records to display per page (defaults to 20)
28600      */
28601     pageSize: 20,
28602     /**
28603      * @cfg {String} displayMsg
28604      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28605      */
28606     displayMsg : 'Displaying {0} - {1} of {2}',
28607     /**
28608      * @cfg {String} emptyMsg
28609      * The message to display when no records are found (defaults to "No data to display")
28610      */
28611     emptyMsg : 'No data to display',
28612     /**
28613      * Customizable piece of the default paging text (defaults to "Page")
28614      * @type String
28615      */
28616     beforePageText : "Page",
28617     /**
28618      * Customizable piece of the default paging text (defaults to "of %0")
28619      * @type String
28620      */
28621     afterPageText : "of {0}",
28622     /**
28623      * Customizable piece of the default paging text (defaults to "First Page")
28624      * @type String
28625      */
28626     firstText : "First Page",
28627     /**
28628      * Customizable piece of the default paging text (defaults to "Previous Page")
28629      * @type String
28630      */
28631     prevText : "Previous Page",
28632     /**
28633      * Customizable piece of the default paging text (defaults to "Next Page")
28634      * @type String
28635      */
28636     nextText : "Next Page",
28637     /**
28638      * Customizable piece of the default paging text (defaults to "Last Page")
28639      * @type String
28640      */
28641     lastText : "Last Page",
28642     /**
28643      * Customizable piece of the default paging text (defaults to "Refresh")
28644      * @type String
28645      */
28646     refreshText : "Refresh",
28647
28648     buttons : false,
28649     // private
28650     onRender : function(ct, position) 
28651     {
28652         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28653         this.navgroup.parentId = this.id;
28654         this.navgroup.onRender(this.el, null);
28655         // add the buttons to the navgroup
28656         
28657         if(this.displayInfo){
28658             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28659             this.displayEl = this.el.select('.x-paging-info', true).first();
28660 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28661 //            this.displayEl = navel.el.select('span',true).first();
28662         }
28663         
28664         var _this = this;
28665         
28666         if(this.buttons){
28667             Roo.each(_this.buttons, function(e){ // this might need to use render????
28668                Roo.factory(e).render(_this.el);
28669             });
28670         }
28671             
28672         Roo.each(_this.toolbarItems, function(e) {
28673             _this.navgroup.addItem(e);
28674         });
28675         
28676         
28677         this.first = this.navgroup.addItem({
28678             tooltip: this.firstText,
28679             cls: "prev btn-outline-secondary",
28680             html : ' <i class="fa fa-step-backward"></i>',
28681             disabled: true,
28682             preventDefault: true,
28683             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28684         });
28685         
28686         this.prev =  this.navgroup.addItem({
28687             tooltip: this.prevText,
28688             cls: "prev btn-outline-secondary",
28689             html : ' <i class="fa fa-backward"></i>',
28690             disabled: true,
28691             preventDefault: true,
28692             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28693         });
28694     //this.addSeparator();
28695         
28696         
28697         var field = this.navgroup.addItem( {
28698             tagtype : 'span',
28699             cls : 'x-paging-position  btn-outline-secondary',
28700              disabled: true,
28701             html : this.beforePageText  +
28702                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28703                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28704          } ); //?? escaped?
28705         
28706         this.field = field.el.select('input', true).first();
28707         this.field.on("keydown", this.onPagingKeydown, this);
28708         this.field.on("focus", function(){this.dom.select();});
28709     
28710     
28711         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28712         //this.field.setHeight(18);
28713         //this.addSeparator();
28714         this.next = this.navgroup.addItem({
28715             tooltip: this.nextText,
28716             cls: "next btn-outline-secondary",
28717             html : ' <i class="fa fa-forward"></i>',
28718             disabled: true,
28719             preventDefault: true,
28720             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28721         });
28722         this.last = this.navgroup.addItem({
28723             tooltip: this.lastText,
28724             html : ' <i class="fa fa-step-forward"></i>',
28725             cls: "next btn-outline-secondary",
28726             disabled: true,
28727             preventDefault: true,
28728             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28729         });
28730     //this.addSeparator();
28731         this.loading = this.navgroup.addItem({
28732             tooltip: this.refreshText,
28733             cls: "btn-outline-secondary",
28734             html : ' <i class="fa fa-refresh"></i>',
28735             preventDefault: true,
28736             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28737         });
28738         
28739     },
28740
28741     // private
28742     updateInfo : function(){
28743         if(this.displayEl){
28744             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28745             var msg = count == 0 ?
28746                 this.emptyMsg :
28747                 String.format(
28748                     this.displayMsg,
28749                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28750                 );
28751             this.displayEl.update(msg);
28752         }
28753     },
28754
28755     // private
28756     onLoad : function(ds, r, o)
28757     {
28758         this.cursor = o.params && o.params.start ? o.params.start : 0;
28759         
28760         var d = this.getPageData(),
28761             ap = d.activePage,
28762             ps = d.pages;
28763         
28764         
28765         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28766         this.field.dom.value = ap;
28767         this.first.setDisabled(ap == 1);
28768         this.prev.setDisabled(ap == 1);
28769         this.next.setDisabled(ap == ps);
28770         this.last.setDisabled(ap == ps);
28771         this.loading.enable();
28772         this.updateInfo();
28773     },
28774
28775     // private
28776     getPageData : function(){
28777         var total = this.ds.getTotalCount();
28778         return {
28779             total : total,
28780             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28781             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28782         };
28783     },
28784
28785     // private
28786     onLoadError : function(o){
28787         this.loading.enable();
28788         if (this.ds.events.loadexception.listeners.length  < 2) {
28789             // nothing has been assigned to loadexception except this...
28790             // so 
28791             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
28792
28793         }
28794     },
28795
28796     // private
28797     onPagingKeydown : function(e){
28798         var k = e.getKey();
28799         var d = this.getPageData();
28800         if(k == e.RETURN){
28801             var v = this.field.dom.value, pageNum;
28802             if(!v || isNaN(pageNum = parseInt(v, 10))){
28803                 this.field.dom.value = d.activePage;
28804                 return;
28805             }
28806             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28807             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28808             e.stopEvent();
28809         }
28810         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))
28811         {
28812           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28813           this.field.dom.value = pageNum;
28814           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28815           e.stopEvent();
28816         }
28817         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28818         {
28819           var v = this.field.dom.value, pageNum; 
28820           var increment = (e.shiftKey) ? 10 : 1;
28821           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28822                 increment *= -1;
28823           }
28824           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28825             this.field.dom.value = d.activePage;
28826             return;
28827           }
28828           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28829           {
28830             this.field.dom.value = parseInt(v, 10) + increment;
28831             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28832             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28833           }
28834           e.stopEvent();
28835         }
28836     },
28837
28838     // private
28839     beforeLoad : function(){
28840         if(this.loading){
28841             this.loading.disable();
28842         }
28843     },
28844
28845     // private
28846     onClick : function(which){
28847         
28848         var ds = this.ds;
28849         if (!ds) {
28850             return;
28851         }
28852         
28853         switch(which){
28854             case "first":
28855                 ds.load({params:{start: 0, limit: this.pageSize}});
28856             break;
28857             case "prev":
28858                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28859             break;
28860             case "next":
28861                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28862             break;
28863             case "last":
28864                 var total = ds.getTotalCount();
28865                 var extra = total % this.pageSize;
28866                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28867                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28868             break;
28869             case "refresh":
28870                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28871             break;
28872         }
28873     },
28874
28875     /**
28876      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28877      * @param {Roo.data.Store} store The data store to unbind
28878      */
28879     unbind : function(ds){
28880         ds.un("beforeload", this.beforeLoad, this);
28881         ds.un("load", this.onLoad, this);
28882         ds.un("loadexception", this.onLoadError, this);
28883         ds.un("remove", this.updateInfo, this);
28884         ds.un("add", this.updateInfo, this);
28885         this.ds = undefined;
28886     },
28887
28888     /**
28889      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28890      * @param {Roo.data.Store} store The data store to bind
28891      */
28892     bind : function(ds){
28893         ds.on("beforeload", this.beforeLoad, this);
28894         ds.on("load", this.onLoad, this);
28895         ds.on("loadexception", this.onLoadError, this);
28896         ds.on("remove", this.updateInfo, this);
28897         ds.on("add", this.updateInfo, this);
28898         this.ds = ds;
28899     }
28900 });/*
28901  * - LGPL
28902  *
28903  * element
28904  * 
28905  */
28906
28907 /**
28908  * @class Roo.bootstrap.MessageBar
28909  * @extends Roo.bootstrap.Component
28910  * Bootstrap MessageBar class
28911  * @cfg {String} html contents of the MessageBar
28912  * @cfg {String} weight (info | success | warning | danger) default info
28913  * @cfg {String} beforeClass insert the bar before the given class
28914  * @cfg {Boolean} closable (true | false) default false
28915  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28916  * 
28917  * @constructor
28918  * Create a new Element
28919  * @param {Object} config The config object
28920  */
28921
28922 Roo.bootstrap.MessageBar = function(config){
28923     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28924 };
28925
28926 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28927     
28928     html: '',
28929     weight: 'info',
28930     closable: false,
28931     fixed: false,
28932     beforeClass: 'bootstrap-sticky-wrap',
28933     
28934     getAutoCreate : function(){
28935         
28936         var cfg = {
28937             tag: 'div',
28938             cls: 'alert alert-dismissable alert-' + this.weight,
28939             cn: [
28940                 {
28941                     tag: 'span',
28942                     cls: 'message',
28943                     html: this.html || ''
28944                 }
28945             ]
28946         };
28947         
28948         if(this.fixed){
28949             cfg.cls += ' alert-messages-fixed';
28950         }
28951         
28952         if(this.closable){
28953             cfg.cn.push({
28954                 tag: 'button',
28955                 cls: 'close',
28956                 html: 'x'
28957             });
28958         }
28959         
28960         return cfg;
28961     },
28962     
28963     onRender : function(ct, position)
28964     {
28965         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28966         
28967         if(!this.el){
28968             var cfg = Roo.apply({},  this.getAutoCreate());
28969             cfg.id = Roo.id();
28970             
28971             if (this.cls) {
28972                 cfg.cls += ' ' + this.cls;
28973             }
28974             if (this.style) {
28975                 cfg.style = this.style;
28976             }
28977             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28978             
28979             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28980         }
28981         
28982         this.el.select('>button.close').on('click', this.hide, this);
28983         
28984     },
28985     
28986     show : function()
28987     {
28988         if (!this.rendered) {
28989             this.render();
28990         }
28991         
28992         this.el.show();
28993         
28994         this.fireEvent('show', this);
28995         
28996     },
28997     
28998     hide : function()
28999     {
29000         if (!this.rendered) {
29001             this.render();
29002         }
29003         
29004         this.el.hide();
29005         
29006         this.fireEvent('hide', this);
29007     },
29008     
29009     update : function()
29010     {
29011 //        var e = this.el.dom.firstChild;
29012 //        
29013 //        if(this.closable){
29014 //            e = e.nextSibling;
29015 //        }
29016 //        
29017 //        e.data = this.html || '';
29018
29019         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29020     }
29021    
29022 });
29023
29024  
29025
29026      /*
29027  * - LGPL
29028  *
29029  * Graph
29030  * 
29031  */
29032
29033
29034 /**
29035  * @class Roo.bootstrap.Graph
29036  * @extends Roo.bootstrap.Component
29037  * Bootstrap Graph class
29038 > Prameters
29039  -sm {number} sm 4
29040  -md {number} md 5
29041  @cfg {String} graphtype  bar | vbar | pie
29042  @cfg {number} g_x coodinator | centre x (pie)
29043  @cfg {number} g_y coodinator | centre y (pie)
29044  @cfg {number} g_r radius (pie)
29045  @cfg {number} g_height height of the chart (respected by all elements in the set)
29046  @cfg {number} g_width width of the chart (respected by all elements in the set)
29047  @cfg {Object} title The title of the chart
29048     
29049  -{Array}  values
29050  -opts (object) options for the chart 
29051      o {
29052      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29053      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29054      o vgutter (number)
29055      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.
29056      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29057      o to
29058      o stretch (boolean)
29059      o }
29060  -opts (object) options for the pie
29061      o{
29062      o cut
29063      o startAngle (number)
29064      o endAngle (number)
29065      } 
29066  *
29067  * @constructor
29068  * Create a new Input
29069  * @param {Object} config The config object
29070  */
29071
29072 Roo.bootstrap.Graph = function(config){
29073     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29074     
29075     this.addEvents({
29076         // img events
29077         /**
29078          * @event click
29079          * The img click event for the img.
29080          * @param {Roo.EventObject} e
29081          */
29082         "click" : true
29083     });
29084 };
29085
29086 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29087     
29088     sm: 4,
29089     md: 5,
29090     graphtype: 'bar',
29091     g_height: 250,
29092     g_width: 400,
29093     g_x: 50,
29094     g_y: 50,
29095     g_r: 30,
29096     opts:{
29097         //g_colors: this.colors,
29098         g_type: 'soft',
29099         g_gutter: '20%'
29100
29101     },
29102     title : false,
29103
29104     getAutoCreate : function(){
29105         
29106         var cfg = {
29107             tag: 'div',
29108             html : null
29109         };
29110         
29111         
29112         return  cfg;
29113     },
29114
29115     onRender : function(ct,position){
29116         
29117         
29118         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29119         
29120         if (typeof(Raphael) == 'undefined') {
29121             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29122             return;
29123         }
29124         
29125         this.raphael = Raphael(this.el.dom);
29126         
29127                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29128                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29129                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29130                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29131                 /*
29132                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29133                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29134                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29135                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29136                 
29137                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29138                 r.barchart(330, 10, 300, 220, data1);
29139                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29140                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29141                 */
29142                 
29143                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29144                 // r.barchart(30, 30, 560, 250,  xdata, {
29145                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29146                 //     axis : "0 0 1 1",
29147                 //     axisxlabels :  xdata
29148                 //     //yvalues : cols,
29149                    
29150                 // });
29151 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29152 //        
29153 //        this.load(null,xdata,{
29154 //                axis : "0 0 1 1",
29155 //                axisxlabels :  xdata
29156 //                });
29157
29158     },
29159
29160     load : function(graphtype,xdata,opts)
29161     {
29162         this.raphael.clear();
29163         if(!graphtype) {
29164             graphtype = this.graphtype;
29165         }
29166         if(!opts){
29167             opts = this.opts;
29168         }
29169         var r = this.raphael,
29170             fin = function () {
29171                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29172             },
29173             fout = function () {
29174                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29175             },
29176             pfin = function() {
29177                 this.sector.stop();
29178                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29179
29180                 if (this.label) {
29181                     this.label[0].stop();
29182                     this.label[0].attr({ r: 7.5 });
29183                     this.label[1].attr({ "font-weight": 800 });
29184                 }
29185             },
29186             pfout = function() {
29187                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29188
29189                 if (this.label) {
29190                     this.label[0].animate({ r: 5 }, 500, "bounce");
29191                     this.label[1].attr({ "font-weight": 400 });
29192                 }
29193             };
29194
29195         switch(graphtype){
29196             case 'bar':
29197                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29198                 break;
29199             case 'hbar':
29200                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29201                 break;
29202             case 'pie':
29203 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29204 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29205 //            
29206                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29207                 
29208                 break;
29209
29210         }
29211         
29212         if(this.title){
29213             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29214         }
29215         
29216     },
29217     
29218     setTitle: function(o)
29219     {
29220         this.title = o;
29221     },
29222     
29223     initEvents: function() {
29224         
29225         if(!this.href){
29226             this.el.on('click', this.onClick, this);
29227         }
29228     },
29229     
29230     onClick : function(e)
29231     {
29232         Roo.log('img onclick');
29233         this.fireEvent('click', this, e);
29234     }
29235    
29236 });
29237
29238  
29239 /*
29240  * - LGPL
29241  *
29242  * numberBox
29243  * 
29244  */
29245 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29246
29247 /**
29248  * @class Roo.bootstrap.dash.NumberBox
29249  * @extends Roo.bootstrap.Component
29250  * Bootstrap NumberBox class
29251  * @cfg {String} headline Box headline
29252  * @cfg {String} content Box content
29253  * @cfg {String} icon Box icon
29254  * @cfg {String} footer Footer text
29255  * @cfg {String} fhref Footer href
29256  * 
29257  * @constructor
29258  * Create a new NumberBox
29259  * @param {Object} config The config object
29260  */
29261
29262
29263 Roo.bootstrap.dash.NumberBox = function(config){
29264     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29265     
29266 };
29267
29268 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29269     
29270     headline : '',
29271     content : '',
29272     icon : '',
29273     footer : '',
29274     fhref : '',
29275     ficon : '',
29276     
29277     getAutoCreate : function(){
29278         
29279         var cfg = {
29280             tag : 'div',
29281             cls : 'small-box ',
29282             cn : [
29283                 {
29284                     tag : 'div',
29285                     cls : 'inner',
29286                     cn :[
29287                         {
29288                             tag : 'h3',
29289                             cls : 'roo-headline',
29290                             html : this.headline
29291                         },
29292                         {
29293                             tag : 'p',
29294                             cls : 'roo-content',
29295                             html : this.content
29296                         }
29297                     ]
29298                 }
29299             ]
29300         };
29301         
29302         if(this.icon){
29303             cfg.cn.push({
29304                 tag : 'div',
29305                 cls : 'icon',
29306                 cn :[
29307                     {
29308                         tag : 'i',
29309                         cls : 'ion ' + this.icon
29310                     }
29311                 ]
29312             });
29313         }
29314         
29315         if(this.footer){
29316             var footer = {
29317                 tag : 'a',
29318                 cls : 'small-box-footer',
29319                 href : this.fhref || '#',
29320                 html : this.footer
29321             };
29322             
29323             cfg.cn.push(footer);
29324             
29325         }
29326         
29327         return  cfg;
29328     },
29329
29330     onRender : function(ct,position){
29331         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29332
29333
29334        
29335                 
29336     },
29337
29338     setHeadline: function (value)
29339     {
29340         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29341     },
29342     
29343     setFooter: function (value, href)
29344     {
29345         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29346         
29347         if(href){
29348             this.el.select('a.small-box-footer',true).first().attr('href', href);
29349         }
29350         
29351     },
29352
29353     setContent: function (value)
29354     {
29355         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29356     },
29357
29358     initEvents: function() 
29359     {   
29360         
29361     }
29362     
29363 });
29364
29365  
29366 /*
29367  * - LGPL
29368  *
29369  * TabBox
29370  * 
29371  */
29372 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29373
29374 /**
29375  * @class Roo.bootstrap.dash.TabBox
29376  * @extends Roo.bootstrap.Component
29377  * @children Roo.bootstrap.dash.TabPane
29378  * Bootstrap TabBox class
29379  * @cfg {String} title Title of the TabBox
29380  * @cfg {String} icon Icon of the TabBox
29381  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29382  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29383  * 
29384  * @constructor
29385  * Create a new TabBox
29386  * @param {Object} config The config object
29387  */
29388
29389
29390 Roo.bootstrap.dash.TabBox = function(config){
29391     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29392     this.addEvents({
29393         // raw events
29394         /**
29395          * @event addpane
29396          * When a pane is added
29397          * @param {Roo.bootstrap.dash.TabPane} pane
29398          */
29399         "addpane" : true,
29400         /**
29401          * @event activatepane
29402          * When a pane is activated
29403          * @param {Roo.bootstrap.dash.TabPane} pane
29404          */
29405         "activatepane" : true
29406         
29407          
29408     });
29409     
29410     this.panes = [];
29411 };
29412
29413 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29414
29415     title : '',
29416     icon : false,
29417     showtabs : true,
29418     tabScrollable : false,
29419     
29420     getChildContainer : function()
29421     {
29422         return this.el.select('.tab-content', true).first();
29423     },
29424     
29425     getAutoCreate : function(){
29426         
29427         var header = {
29428             tag: 'li',
29429             cls: 'pull-left header',
29430             html: this.title,
29431             cn : []
29432         };
29433         
29434         if(this.icon){
29435             header.cn.push({
29436                 tag: 'i',
29437                 cls: 'fa ' + this.icon
29438             });
29439         }
29440         
29441         var h = {
29442             tag: 'ul',
29443             cls: 'nav nav-tabs pull-right',
29444             cn: [
29445                 header
29446             ]
29447         };
29448         
29449         if(this.tabScrollable){
29450             h = {
29451                 tag: 'div',
29452                 cls: 'tab-header',
29453                 cn: [
29454                     {
29455                         tag: 'ul',
29456                         cls: 'nav nav-tabs pull-right',
29457                         cn: [
29458                             header
29459                         ]
29460                     }
29461                 ]
29462             };
29463         }
29464         
29465         var cfg = {
29466             tag: 'div',
29467             cls: 'nav-tabs-custom',
29468             cn: [
29469                 h,
29470                 {
29471                     tag: 'div',
29472                     cls: 'tab-content no-padding',
29473                     cn: []
29474                 }
29475             ]
29476         };
29477
29478         return  cfg;
29479     },
29480     initEvents : function()
29481     {
29482         //Roo.log('add add pane handler');
29483         this.on('addpane', this.onAddPane, this);
29484     },
29485      /**
29486      * Updates the box title
29487      * @param {String} html to set the title to.
29488      */
29489     setTitle : function(value)
29490     {
29491         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29492     },
29493     onAddPane : function(pane)
29494     {
29495         this.panes.push(pane);
29496         //Roo.log('addpane');
29497         //Roo.log(pane);
29498         // tabs are rendere left to right..
29499         if(!this.showtabs){
29500             return;
29501         }
29502         
29503         var ctr = this.el.select('.nav-tabs', true).first();
29504          
29505          
29506         var existing = ctr.select('.nav-tab',true);
29507         var qty = existing.getCount();;
29508         
29509         
29510         var tab = ctr.createChild({
29511             tag : 'li',
29512             cls : 'nav-tab' + (qty ? '' : ' active'),
29513             cn : [
29514                 {
29515                     tag : 'a',
29516                     href:'#',
29517                     html : pane.title
29518                 }
29519             ]
29520         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29521         pane.tab = tab;
29522         
29523         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29524         if (!qty) {
29525             pane.el.addClass('active');
29526         }
29527         
29528                 
29529     },
29530     onTabClick : function(ev,un,ob,pane)
29531     {
29532         //Roo.log('tab - prev default');
29533         ev.preventDefault();
29534         
29535         
29536         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29537         pane.tab.addClass('active');
29538         //Roo.log(pane.title);
29539         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29540         // technically we should have a deactivate event.. but maybe add later.
29541         // and it should not de-activate the selected tab...
29542         this.fireEvent('activatepane', pane);
29543         pane.el.addClass('active');
29544         pane.fireEvent('activate');
29545         
29546         
29547     },
29548     
29549     getActivePane : function()
29550     {
29551         var r = false;
29552         Roo.each(this.panes, function(p) {
29553             if(p.el.hasClass('active')){
29554                 r = p;
29555                 return false;
29556             }
29557             
29558             return;
29559         });
29560         
29561         return r;
29562     }
29563     
29564     
29565 });
29566
29567  
29568 /*
29569  * - LGPL
29570  *
29571  * Tab pane
29572  * 
29573  */
29574 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29575 /**
29576  * @class Roo.bootstrap.TabPane
29577  * @extends Roo.bootstrap.Component
29578  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29579  * Bootstrap TabPane class
29580  * @cfg {Boolean} active (false | true) Default false
29581  * @cfg {String} title title of panel
29582
29583  * 
29584  * @constructor
29585  * Create a new TabPane
29586  * @param {Object} config The config object
29587  */
29588
29589 Roo.bootstrap.dash.TabPane = function(config){
29590     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29591     
29592     this.addEvents({
29593         // raw events
29594         /**
29595          * @event activate
29596          * When a pane is activated
29597          * @param {Roo.bootstrap.dash.TabPane} pane
29598          */
29599         "activate" : true
29600          
29601     });
29602 };
29603
29604 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29605     
29606     active : false,
29607     title : '',
29608     
29609     // the tabBox that this is attached to.
29610     tab : false,
29611      
29612     getAutoCreate : function() 
29613     {
29614         var cfg = {
29615             tag: 'div',
29616             cls: 'tab-pane'
29617         };
29618         
29619         if(this.active){
29620             cfg.cls += ' active';
29621         }
29622         
29623         return cfg;
29624     },
29625     initEvents  : function()
29626     {
29627         //Roo.log('trigger add pane handler');
29628         this.parent().fireEvent('addpane', this)
29629     },
29630     
29631      /**
29632      * Updates the tab title 
29633      * @param {String} html to set the title to.
29634      */
29635     setTitle: function(str)
29636     {
29637         if (!this.tab) {
29638             return;
29639         }
29640         this.title = str;
29641         this.tab.select('a', true).first().dom.innerHTML = str;
29642         
29643     }
29644     
29645     
29646     
29647 });
29648
29649  
29650
29651
29652  /*
29653  * - LGPL
29654  *
29655  * Tooltip
29656  * 
29657  */
29658
29659 /**
29660  * @class Roo.bootstrap.Tooltip
29661  * Bootstrap Tooltip class
29662  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29663  * to determine which dom element triggers the tooltip.
29664  * 
29665  * It needs to add support for additional attributes like tooltip-position
29666  * 
29667  * @constructor
29668  * Create a new Toolti
29669  * @param {Object} config The config object
29670  */
29671
29672 Roo.bootstrap.Tooltip = function(config){
29673     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29674     
29675     this.alignment = Roo.bootstrap.Tooltip.alignment;
29676     
29677     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29678         this.alignment = config.alignment;
29679     }
29680     
29681 };
29682
29683 Roo.apply(Roo.bootstrap.Tooltip, {
29684     /**
29685      * @function init initialize tooltip monitoring.
29686      * @static
29687      */
29688     currentEl : false,
29689     currentTip : false,
29690     currentRegion : false,
29691     
29692     //  init : delay?
29693     
29694     init : function()
29695     {
29696         Roo.get(document).on('mouseover', this.enter ,this);
29697         Roo.get(document).on('mouseout', this.leave, this);
29698          
29699         
29700         this.currentTip = new Roo.bootstrap.Tooltip();
29701     },
29702     
29703     enter : function(ev)
29704     {
29705         var dom = ev.getTarget();
29706         
29707         //Roo.log(['enter',dom]);
29708         var el = Roo.fly(dom);
29709         if (this.currentEl) {
29710             //Roo.log(dom);
29711             //Roo.log(this.currentEl);
29712             //Roo.log(this.currentEl.contains(dom));
29713             if (this.currentEl == el) {
29714                 return;
29715             }
29716             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29717                 return;
29718             }
29719
29720         }
29721         
29722         if (this.currentTip.el) {
29723             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29724         }    
29725         //Roo.log(ev);
29726         
29727         if(!el || el.dom == document){
29728             return;
29729         }
29730         
29731         var bindEl = el; 
29732         var pel = false;
29733         if (!el.attr('tooltip')) {
29734             pel = el.findParent("[tooltip]");
29735             if (pel) {
29736                 bindEl = Roo.get(pel);
29737             }
29738         }
29739         
29740        
29741         
29742         // you can not look for children, as if el is the body.. then everythign is the child..
29743         if (!pel && !el.attr('tooltip')) { //
29744             if (!el.select("[tooltip]").elements.length) {
29745                 return;
29746             }
29747             // is the mouse over this child...?
29748             bindEl = el.select("[tooltip]").first();
29749             var xy = ev.getXY();
29750             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29751                 //Roo.log("not in region.");
29752                 return;
29753             }
29754             //Roo.log("child element over..");
29755             
29756         }
29757         this.currentEl = el;
29758         this.currentTip.bind(bindEl);
29759         this.currentRegion = Roo.lib.Region.getRegion(dom);
29760         this.currentTip.enter();
29761         
29762     },
29763     leave : function(ev)
29764     {
29765         var dom = ev.getTarget();
29766         //Roo.log(['leave',dom]);
29767         if (!this.currentEl) {
29768             return;
29769         }
29770         
29771         
29772         if (dom != this.currentEl.dom) {
29773             return;
29774         }
29775         var xy = ev.getXY();
29776         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29777             return;
29778         }
29779         // only activate leave if mouse cursor is outside... bounding box..
29780         
29781         
29782         
29783         
29784         if (this.currentTip) {
29785             this.currentTip.leave();
29786         }
29787         //Roo.log('clear currentEl');
29788         this.currentEl = false;
29789         
29790         
29791     },
29792     alignment : {
29793         'left' : ['r-l', [-2,0], 'right'],
29794         'right' : ['l-r', [2,0], 'left'],
29795         'bottom' : ['t-b', [0,2], 'top'],
29796         'top' : [ 'b-t', [0,-2], 'bottom']
29797     }
29798     
29799 });
29800
29801
29802 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29803     
29804     
29805     bindEl : false,
29806     
29807     delay : null, // can be { show : 300 , hide: 500}
29808     
29809     timeout : null,
29810     
29811     hoverState : null, //???
29812     
29813     placement : 'bottom', 
29814     
29815     alignment : false,
29816     
29817     getAutoCreate : function(){
29818     
29819         var cfg = {
29820            cls : 'tooltip',   
29821            role : 'tooltip',
29822            cn : [
29823                 {
29824                     cls : 'tooltip-arrow arrow'
29825                 },
29826                 {
29827                     cls : 'tooltip-inner'
29828                 }
29829            ]
29830         };
29831         
29832         return cfg;
29833     },
29834     bind : function(el)
29835     {
29836         this.bindEl = el;
29837     },
29838     
29839     initEvents : function()
29840     {
29841         this.arrowEl = this.el.select('.arrow', true).first();
29842         this.innerEl = this.el.select('.tooltip-inner', true).first();
29843     },
29844     
29845     enter : function () {
29846        
29847         if (this.timeout != null) {
29848             clearTimeout(this.timeout);
29849         }
29850         
29851         this.hoverState = 'in';
29852          //Roo.log("enter - show");
29853         if (!this.delay || !this.delay.show) {
29854             this.show();
29855             return;
29856         }
29857         var _t = this;
29858         this.timeout = setTimeout(function () {
29859             if (_t.hoverState == 'in') {
29860                 _t.show();
29861             }
29862         }, this.delay.show);
29863     },
29864     leave : function()
29865     {
29866         clearTimeout(this.timeout);
29867     
29868         this.hoverState = 'out';
29869          if (!this.delay || !this.delay.hide) {
29870             this.hide();
29871             return;
29872         }
29873        
29874         var _t = this;
29875         this.timeout = setTimeout(function () {
29876             //Roo.log("leave - timeout");
29877             
29878             if (_t.hoverState == 'out') {
29879                 _t.hide();
29880                 Roo.bootstrap.Tooltip.currentEl = false;
29881             }
29882         }, delay);
29883     },
29884     
29885     show : function (msg)
29886     {
29887         if (!this.el) {
29888             this.render(document.body);
29889         }
29890         // set content.
29891         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29892         
29893         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29894         
29895         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29896         
29897         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29898                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29899         
29900         var placement = typeof this.placement == 'function' ?
29901             this.placement.call(this, this.el, on_el) :
29902             this.placement;
29903             
29904         var autoToken = /\s?auto?\s?/i;
29905         var autoPlace = autoToken.test(placement);
29906         if (autoPlace) {
29907             placement = placement.replace(autoToken, '') || 'top';
29908         }
29909         
29910         //this.el.detach()
29911         //this.el.setXY([0,0]);
29912         this.el.show();
29913         //this.el.dom.style.display='block';
29914         
29915         //this.el.appendTo(on_el);
29916         
29917         var p = this.getPosition();
29918         var box = this.el.getBox();
29919         
29920         if (autoPlace) {
29921             // fixme..
29922         }
29923         
29924         var align = this.alignment[placement];
29925         
29926         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29927         
29928         if(placement == 'top' || placement == 'bottom'){
29929             if(xy[0] < 0){
29930                 placement = 'right';
29931             }
29932             
29933             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29934                 placement = 'left';
29935             }
29936             
29937             var scroll = Roo.select('body', true).first().getScroll();
29938             
29939             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29940                 placement = 'top';
29941             }
29942             
29943             align = this.alignment[placement];
29944             
29945             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29946             
29947         }
29948         
29949         var elems = document.getElementsByTagName('div');
29950         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29951         for (var i = 0; i < elems.length; i++) {
29952           var zindex = Number.parseInt(
29953                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29954                 10
29955           );
29956           if (zindex > highest) {
29957             highest = zindex;
29958           }
29959         }
29960         
29961         
29962         
29963         this.el.dom.style.zIndex = highest;
29964         
29965         this.el.alignTo(this.bindEl, align[0],align[1]);
29966         //var arrow = this.el.select('.arrow',true).first();
29967         //arrow.set(align[2], 
29968         
29969         this.el.addClass(placement);
29970         this.el.addClass("bs-tooltip-"+ placement);
29971         
29972         this.el.addClass('in fade show');
29973         
29974         this.hoverState = null;
29975         
29976         if (this.el.hasClass('fade')) {
29977             // fade it?
29978         }
29979         
29980         
29981         
29982         
29983         
29984     },
29985     hide : function()
29986     {
29987          
29988         if (!this.el) {
29989             return;
29990         }
29991         //this.el.setXY([0,0]);
29992         this.el.removeClass(['show', 'in']);
29993         //this.el.hide();
29994         
29995     }
29996     
29997 });
29998  
29999
30000  /*
30001  * - LGPL
30002  *
30003  * Location Picker
30004  * 
30005  */
30006
30007 /**
30008  * @class Roo.bootstrap.LocationPicker
30009  * @extends Roo.bootstrap.Component
30010  * Bootstrap LocationPicker class
30011  * @cfg {Number} latitude Position when init default 0
30012  * @cfg {Number} longitude Position when init default 0
30013  * @cfg {Number} zoom default 15
30014  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30015  * @cfg {Boolean} mapTypeControl default false
30016  * @cfg {Boolean} disableDoubleClickZoom default false
30017  * @cfg {Boolean} scrollwheel default true
30018  * @cfg {Boolean} streetViewControl default false
30019  * @cfg {Number} radius default 0
30020  * @cfg {String} locationName
30021  * @cfg {Boolean} draggable default true
30022  * @cfg {Boolean} enableAutocomplete default false
30023  * @cfg {Boolean} enableReverseGeocode default true
30024  * @cfg {String} markerTitle
30025  * 
30026  * @constructor
30027  * Create a new LocationPicker
30028  * @param {Object} config The config object
30029  */
30030
30031
30032 Roo.bootstrap.LocationPicker = function(config){
30033     
30034     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30035     
30036     this.addEvents({
30037         /**
30038          * @event initial
30039          * Fires when the picker initialized.
30040          * @param {Roo.bootstrap.LocationPicker} this
30041          * @param {Google Location} location
30042          */
30043         initial : true,
30044         /**
30045          * @event positionchanged
30046          * Fires when the picker position changed.
30047          * @param {Roo.bootstrap.LocationPicker} this
30048          * @param {Google Location} location
30049          */
30050         positionchanged : true,
30051         /**
30052          * @event resize
30053          * Fires when the map resize.
30054          * @param {Roo.bootstrap.LocationPicker} this
30055          */
30056         resize : true,
30057         /**
30058          * @event show
30059          * Fires when the map show.
30060          * @param {Roo.bootstrap.LocationPicker} this
30061          */
30062         show : true,
30063         /**
30064          * @event hide
30065          * Fires when the map hide.
30066          * @param {Roo.bootstrap.LocationPicker} this
30067          */
30068         hide : true,
30069         /**
30070          * @event mapClick
30071          * Fires when click the map.
30072          * @param {Roo.bootstrap.LocationPicker} this
30073          * @param {Map event} e
30074          */
30075         mapClick : true,
30076         /**
30077          * @event mapRightClick
30078          * Fires when right click the map.
30079          * @param {Roo.bootstrap.LocationPicker} this
30080          * @param {Map event} e
30081          */
30082         mapRightClick : true,
30083         /**
30084          * @event markerClick
30085          * Fires when click the marker.
30086          * @param {Roo.bootstrap.LocationPicker} this
30087          * @param {Map event} e
30088          */
30089         markerClick : true,
30090         /**
30091          * @event markerRightClick
30092          * Fires when right click the marker.
30093          * @param {Roo.bootstrap.LocationPicker} this
30094          * @param {Map event} e
30095          */
30096         markerRightClick : true,
30097         /**
30098          * @event OverlayViewDraw
30099          * Fires when OverlayView Draw
30100          * @param {Roo.bootstrap.LocationPicker} this
30101          */
30102         OverlayViewDraw : true,
30103         /**
30104          * @event OverlayViewOnAdd
30105          * Fires when OverlayView Draw
30106          * @param {Roo.bootstrap.LocationPicker} this
30107          */
30108         OverlayViewOnAdd : true,
30109         /**
30110          * @event OverlayViewOnRemove
30111          * Fires when OverlayView Draw
30112          * @param {Roo.bootstrap.LocationPicker} this
30113          */
30114         OverlayViewOnRemove : true,
30115         /**
30116          * @event OverlayViewShow
30117          * Fires when OverlayView Draw
30118          * @param {Roo.bootstrap.LocationPicker} this
30119          * @param {Pixel} cpx
30120          */
30121         OverlayViewShow : true,
30122         /**
30123          * @event OverlayViewHide
30124          * Fires when OverlayView Draw
30125          * @param {Roo.bootstrap.LocationPicker} this
30126          */
30127         OverlayViewHide : true,
30128         /**
30129          * @event loadexception
30130          * Fires when load google lib failed.
30131          * @param {Roo.bootstrap.LocationPicker} this
30132          */
30133         loadexception : true
30134     });
30135         
30136 };
30137
30138 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30139     
30140     gMapContext: false,
30141     
30142     latitude: 0,
30143     longitude: 0,
30144     zoom: 15,
30145     mapTypeId: false,
30146     mapTypeControl: false,
30147     disableDoubleClickZoom: false,
30148     scrollwheel: true,
30149     streetViewControl: false,
30150     radius: 0,
30151     locationName: '',
30152     draggable: true,
30153     enableAutocomplete: false,
30154     enableReverseGeocode: true,
30155     markerTitle: '',
30156     
30157     getAutoCreate: function()
30158     {
30159
30160         var cfg = {
30161             tag: 'div',
30162             cls: 'roo-location-picker'
30163         };
30164         
30165         return cfg
30166     },
30167     
30168     initEvents: function(ct, position)
30169     {       
30170         if(!this.el.getWidth() || this.isApplied()){
30171             return;
30172         }
30173         
30174         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30175         
30176         this.initial();
30177     },
30178     
30179     initial: function()
30180     {
30181         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30182             this.fireEvent('loadexception', this);
30183             return;
30184         }
30185         
30186         if(!this.mapTypeId){
30187             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30188         }
30189         
30190         this.gMapContext = this.GMapContext();
30191         
30192         this.initOverlayView();
30193         
30194         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30195         
30196         var _this = this;
30197                 
30198         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30199             _this.setPosition(_this.gMapContext.marker.position);
30200         });
30201         
30202         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30203             _this.fireEvent('mapClick', this, event);
30204             
30205         });
30206
30207         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30208             _this.fireEvent('mapRightClick', this, event);
30209             
30210         });
30211         
30212         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30213             _this.fireEvent('markerClick', this, event);
30214             
30215         });
30216
30217         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30218             _this.fireEvent('markerRightClick', this, event);
30219             
30220         });
30221         
30222         this.setPosition(this.gMapContext.location);
30223         
30224         this.fireEvent('initial', this, this.gMapContext.location);
30225     },
30226     
30227     initOverlayView: function()
30228     {
30229         var _this = this;
30230         
30231         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30232             
30233             draw: function()
30234             {
30235                 _this.fireEvent('OverlayViewDraw', _this);
30236             },
30237             
30238             onAdd: function()
30239             {
30240                 _this.fireEvent('OverlayViewOnAdd', _this);
30241             },
30242             
30243             onRemove: function()
30244             {
30245                 _this.fireEvent('OverlayViewOnRemove', _this);
30246             },
30247             
30248             show: function(cpx)
30249             {
30250                 _this.fireEvent('OverlayViewShow', _this, cpx);
30251             },
30252             
30253             hide: function()
30254             {
30255                 _this.fireEvent('OverlayViewHide', _this);
30256             }
30257             
30258         });
30259     },
30260     
30261     fromLatLngToContainerPixel: function(event)
30262     {
30263         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30264     },
30265     
30266     isApplied: function() 
30267     {
30268         return this.getGmapContext() == false ? false : true;
30269     },
30270     
30271     getGmapContext: function() 
30272     {
30273         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30274     },
30275     
30276     GMapContext: function() 
30277     {
30278         var position = new google.maps.LatLng(this.latitude, this.longitude);
30279         
30280         var _map = new google.maps.Map(this.el.dom, {
30281             center: position,
30282             zoom: this.zoom,
30283             mapTypeId: this.mapTypeId,
30284             mapTypeControl: this.mapTypeControl,
30285             disableDoubleClickZoom: this.disableDoubleClickZoom,
30286             scrollwheel: this.scrollwheel,
30287             streetViewControl: this.streetViewControl,
30288             locationName: this.locationName,
30289             draggable: this.draggable,
30290             enableAutocomplete: this.enableAutocomplete,
30291             enableReverseGeocode: this.enableReverseGeocode
30292         });
30293         
30294         var _marker = new google.maps.Marker({
30295             position: position,
30296             map: _map,
30297             title: this.markerTitle,
30298             draggable: this.draggable
30299         });
30300         
30301         return {
30302             map: _map,
30303             marker: _marker,
30304             circle: null,
30305             location: position,
30306             radius: this.radius,
30307             locationName: this.locationName,
30308             addressComponents: {
30309                 formatted_address: null,
30310                 addressLine1: null,
30311                 addressLine2: null,
30312                 streetName: null,
30313                 streetNumber: null,
30314                 city: null,
30315                 district: null,
30316                 state: null,
30317                 stateOrProvince: null
30318             },
30319             settings: this,
30320             domContainer: this.el.dom,
30321             geodecoder: new google.maps.Geocoder()
30322         };
30323     },
30324     
30325     drawCircle: function(center, radius, options) 
30326     {
30327         if (this.gMapContext.circle != null) {
30328             this.gMapContext.circle.setMap(null);
30329         }
30330         if (radius > 0) {
30331             radius *= 1;
30332             options = Roo.apply({}, options, {
30333                 strokeColor: "#0000FF",
30334                 strokeOpacity: .35,
30335                 strokeWeight: 2,
30336                 fillColor: "#0000FF",
30337                 fillOpacity: .2
30338             });
30339             
30340             options.map = this.gMapContext.map;
30341             options.radius = radius;
30342             options.center = center;
30343             this.gMapContext.circle = new google.maps.Circle(options);
30344             return this.gMapContext.circle;
30345         }
30346         
30347         return null;
30348     },
30349     
30350     setPosition: function(location) 
30351     {
30352         this.gMapContext.location = location;
30353         this.gMapContext.marker.setPosition(location);
30354         this.gMapContext.map.panTo(location);
30355         this.drawCircle(location, this.gMapContext.radius, {});
30356         
30357         var _this = this;
30358         
30359         if (this.gMapContext.settings.enableReverseGeocode) {
30360             this.gMapContext.geodecoder.geocode({
30361                 latLng: this.gMapContext.location
30362             }, function(results, status) {
30363                 
30364                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30365                     _this.gMapContext.locationName = results[0].formatted_address;
30366                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30367                     
30368                     _this.fireEvent('positionchanged', this, location);
30369                 }
30370             });
30371             
30372             return;
30373         }
30374         
30375         this.fireEvent('positionchanged', this, location);
30376     },
30377     
30378     resize: function()
30379     {
30380         google.maps.event.trigger(this.gMapContext.map, "resize");
30381         
30382         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30383         
30384         this.fireEvent('resize', this);
30385     },
30386     
30387     setPositionByLatLng: function(latitude, longitude)
30388     {
30389         this.setPosition(new google.maps.LatLng(latitude, longitude));
30390     },
30391     
30392     getCurrentPosition: function() 
30393     {
30394         return {
30395             latitude: this.gMapContext.location.lat(),
30396             longitude: this.gMapContext.location.lng()
30397         };
30398     },
30399     
30400     getAddressName: function() 
30401     {
30402         return this.gMapContext.locationName;
30403     },
30404     
30405     getAddressComponents: function() 
30406     {
30407         return this.gMapContext.addressComponents;
30408     },
30409     
30410     address_component_from_google_geocode: function(address_components) 
30411     {
30412         var result = {};
30413         
30414         for (var i = 0; i < address_components.length; i++) {
30415             var component = address_components[i];
30416             if (component.types.indexOf("postal_code") >= 0) {
30417                 result.postalCode = component.short_name;
30418             } else if (component.types.indexOf("street_number") >= 0) {
30419                 result.streetNumber = component.short_name;
30420             } else if (component.types.indexOf("route") >= 0) {
30421                 result.streetName = component.short_name;
30422             } else if (component.types.indexOf("neighborhood") >= 0) {
30423                 result.city = component.short_name;
30424             } else if (component.types.indexOf("locality") >= 0) {
30425                 result.city = component.short_name;
30426             } else if (component.types.indexOf("sublocality") >= 0) {
30427                 result.district = component.short_name;
30428             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30429                 result.stateOrProvince = component.short_name;
30430             } else if (component.types.indexOf("country") >= 0) {
30431                 result.country = component.short_name;
30432             }
30433         }
30434         
30435         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30436         result.addressLine2 = "";
30437         return result;
30438     },
30439     
30440     setZoomLevel: function(zoom)
30441     {
30442         this.gMapContext.map.setZoom(zoom);
30443     },
30444     
30445     show: function()
30446     {
30447         if(!this.el){
30448             return;
30449         }
30450         
30451         this.el.show();
30452         
30453         this.resize();
30454         
30455         this.fireEvent('show', this);
30456     },
30457     
30458     hide: function()
30459     {
30460         if(!this.el){
30461             return;
30462         }
30463         
30464         this.el.hide();
30465         
30466         this.fireEvent('hide', this);
30467     }
30468     
30469 });
30470
30471 Roo.apply(Roo.bootstrap.LocationPicker, {
30472     
30473     OverlayView : function(map, options)
30474     {
30475         options = options || {};
30476         
30477         this.setMap(map);
30478     }
30479     
30480     
30481 });/**
30482  * @class Roo.bootstrap.Alert
30483  * @extends Roo.bootstrap.Component
30484  * Bootstrap Alert class - shows an alert area box
30485  * eg
30486  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30487   Enter a valid email address
30488 </div>
30489  * @licence LGPL
30490  * @cfg {String} title The title of alert
30491  * @cfg {String} html The content of alert
30492  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30493  * @cfg {String} fa font-awesomeicon
30494  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30495  * @cfg {Boolean} close true to show a x closer
30496  * 
30497  * 
30498  * @constructor
30499  * Create a new alert
30500  * @param {Object} config The config object
30501  */
30502
30503
30504 Roo.bootstrap.Alert = function(config){
30505     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30506     
30507 };
30508
30509 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30510     
30511     title: '',
30512     html: '',
30513     weight: false,
30514     fa: false,
30515     faicon: false, // BC
30516     close : false,
30517     
30518     
30519     getAutoCreate : function()
30520     {
30521         
30522         var cfg = {
30523             tag : 'div',
30524             cls : 'alert',
30525             cn : [
30526                 {
30527                     tag: 'button',
30528                     type :  "button",
30529                     cls: "close",
30530                     html : '×',
30531                     style : this.close ? '' : 'display:none'
30532                 },
30533                 {
30534                     tag : 'i',
30535                     cls : 'roo-alert-icon'
30536                     
30537                 },
30538                 {
30539                     tag : 'b',
30540                     cls : 'roo-alert-title',
30541                     html : this.title
30542                 },
30543                 {
30544                     tag : 'span',
30545                     cls : 'roo-alert-text',
30546                     html : this.html
30547                 }
30548             ]
30549         };
30550         
30551         if(this.faicon){
30552             cfg.cn[0].cls += ' fa ' + this.faicon;
30553         }
30554         if(this.fa){
30555             cfg.cn[0].cls += ' fa ' + this.fa;
30556         }
30557         
30558         if(this.weight){
30559             cfg.cls += ' alert-' + this.weight;
30560         }
30561         
30562         return cfg;
30563     },
30564     
30565     initEvents: function() 
30566     {
30567         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30568         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30569         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30570         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30571         if (this.seconds > 0) {
30572             this.hide.defer(this.seconds, this);
30573         }
30574     },
30575     /**
30576      * Set the Title Message HTML
30577      * @param {String} html
30578      */
30579     setTitle : function(str)
30580     {
30581         this.titleEl.dom.innerHTML = str;
30582     },
30583      
30584      /**
30585      * Set the Body Message HTML
30586      * @param {String} html
30587      */
30588     setHtml : function(str)
30589     {
30590         this.htmlEl.dom.innerHTML = str;
30591     },
30592     /**
30593      * Set the Weight of the alert
30594      * @param {String} (success|info|warning|danger) weight
30595      */
30596     
30597     setWeight : function(weight)
30598     {
30599         if(this.weight){
30600             this.el.removeClass('alert-' + this.weight);
30601         }
30602         
30603         this.weight = weight;
30604         
30605         this.el.addClass('alert-' + this.weight);
30606     },
30607       /**
30608      * Set the Icon of the alert
30609      * @param {String} see fontawsome names (name without the 'fa-' bit)
30610      */
30611     setIcon : function(icon)
30612     {
30613         if(this.faicon){
30614             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30615         }
30616         
30617         this.faicon = icon;
30618         
30619         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30620     },
30621     /**
30622      * Hide the Alert
30623      */
30624     hide: function() 
30625     {
30626         this.el.hide();   
30627     },
30628     /**
30629      * Show the Alert
30630      */
30631     show: function() 
30632     {  
30633         this.el.show();   
30634     }
30635     
30636 });
30637
30638  
30639 /*
30640 * Licence: LGPL
30641 */
30642
30643 /**
30644  * @class Roo.bootstrap.UploadCropbox
30645  * @extends Roo.bootstrap.Component
30646  * Bootstrap UploadCropbox class
30647  * @cfg {String} emptyText show when image has been loaded
30648  * @cfg {String} rotateNotify show when image too small to rotate
30649  * @cfg {Number} errorTimeout default 3000
30650  * @cfg {Number} minWidth default 300
30651  * @cfg {Number} minHeight default 300
30652  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30653  * @cfg {Boolean} isDocument (true|false) default false
30654  * @cfg {String} url action url
30655  * @cfg {String} paramName default 'imageUpload'
30656  * @cfg {String} method default POST
30657  * @cfg {Boolean} loadMask (true|false) default true
30658  * @cfg {Boolean} loadingText default 'Loading...'
30659  * 
30660  * @constructor
30661  * Create a new UploadCropbox
30662  * @param {Object} config The config object
30663  */
30664
30665 Roo.bootstrap.UploadCropbox = function(config){
30666     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30667     
30668     this.addEvents({
30669         /**
30670          * @event beforeselectfile
30671          * Fire before select file
30672          * @param {Roo.bootstrap.UploadCropbox} this
30673          */
30674         "beforeselectfile" : true,
30675         /**
30676          * @event initial
30677          * Fire after initEvent
30678          * @param {Roo.bootstrap.UploadCropbox} this
30679          */
30680         "initial" : true,
30681         /**
30682          * @event crop
30683          * Fire after initEvent
30684          * @param {Roo.bootstrap.UploadCropbox} this
30685          * @param {String} data
30686          */
30687         "crop" : true,
30688         /**
30689          * @event prepare
30690          * Fire when preparing the file data
30691          * @param {Roo.bootstrap.UploadCropbox} this
30692          * @param {Object} file
30693          */
30694         "prepare" : true,
30695         /**
30696          * @event exception
30697          * Fire when get exception
30698          * @param {Roo.bootstrap.UploadCropbox} this
30699          * @param {XMLHttpRequest} xhr
30700          */
30701         "exception" : true,
30702         /**
30703          * @event beforeloadcanvas
30704          * Fire before load the canvas
30705          * @param {Roo.bootstrap.UploadCropbox} this
30706          * @param {String} src
30707          */
30708         "beforeloadcanvas" : true,
30709         /**
30710          * @event trash
30711          * Fire when trash image
30712          * @param {Roo.bootstrap.UploadCropbox} this
30713          */
30714         "trash" : true,
30715         /**
30716          * @event download
30717          * Fire when download the image
30718          * @param {Roo.bootstrap.UploadCropbox} this
30719          */
30720         "download" : true,
30721         /**
30722          * @event footerbuttonclick
30723          * Fire when footerbuttonclick
30724          * @param {Roo.bootstrap.UploadCropbox} this
30725          * @param {String} type
30726          */
30727         "footerbuttonclick" : true,
30728         /**
30729          * @event resize
30730          * Fire when resize
30731          * @param {Roo.bootstrap.UploadCropbox} this
30732          */
30733         "resize" : true,
30734         /**
30735          * @event rotate
30736          * Fire when rotate the image
30737          * @param {Roo.bootstrap.UploadCropbox} this
30738          * @param {String} pos
30739          */
30740         "rotate" : true,
30741         /**
30742          * @event inspect
30743          * Fire when inspect the file
30744          * @param {Roo.bootstrap.UploadCropbox} this
30745          * @param {Object} file
30746          */
30747         "inspect" : true,
30748         /**
30749          * @event upload
30750          * Fire when xhr upload the file
30751          * @param {Roo.bootstrap.UploadCropbox} this
30752          * @param {Object} data
30753          */
30754         "upload" : true,
30755         /**
30756          * @event arrange
30757          * Fire when arrange the file data
30758          * @param {Roo.bootstrap.UploadCropbox} this
30759          * @param {Object} formData
30760          */
30761         "arrange" : true
30762     });
30763     
30764     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30765 };
30766
30767 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30768     
30769     emptyText : 'Click to upload image',
30770     rotateNotify : 'Image is too small to rotate',
30771     errorTimeout : 3000,
30772     scale : 0,
30773     baseScale : 1,
30774     rotate : 0,
30775     dragable : false,
30776     pinching : false,
30777     mouseX : 0,
30778     mouseY : 0,
30779     cropData : false,
30780     minWidth : 300,
30781     minHeight : 300,
30782     file : false,
30783     exif : {},
30784     baseRotate : 1,
30785     cropType : 'image/jpeg',
30786     buttons : false,
30787     canvasLoaded : false,
30788     isDocument : false,
30789     method : 'POST',
30790     paramName : 'imageUpload',
30791     loadMask : true,
30792     loadingText : 'Loading...',
30793     maskEl : false,
30794     
30795     getAutoCreate : function()
30796     {
30797         var cfg = {
30798             tag : 'div',
30799             cls : 'roo-upload-cropbox',
30800             cn : [
30801                 {
30802                     tag : 'input',
30803                     cls : 'roo-upload-cropbox-selector',
30804                     type : 'file'
30805                 },
30806                 {
30807                     tag : 'div',
30808                     cls : 'roo-upload-cropbox-body',
30809                     style : 'cursor:pointer',
30810                     cn : [
30811                         {
30812                             tag : 'div',
30813                             cls : 'roo-upload-cropbox-preview'
30814                         },
30815                         {
30816                             tag : 'div',
30817                             cls : 'roo-upload-cropbox-thumb'
30818                         },
30819                         {
30820                             tag : 'div',
30821                             cls : 'roo-upload-cropbox-empty-notify',
30822                             html : this.emptyText
30823                         },
30824                         {
30825                             tag : 'div',
30826                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30827                             html : this.rotateNotify
30828                         }
30829                     ]
30830                 },
30831                 {
30832                     tag : 'div',
30833                     cls : 'roo-upload-cropbox-footer',
30834                     cn : {
30835                         tag : 'div',
30836                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30837                         cn : []
30838                     }
30839                 }
30840             ]
30841         };
30842         
30843         return cfg;
30844     },
30845     
30846     onRender : function(ct, position)
30847     {
30848         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30849         
30850         if (this.buttons.length) {
30851             
30852             Roo.each(this.buttons, function(bb) {
30853                 
30854                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30855                 
30856                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30857                 
30858             }, this);
30859         }
30860         
30861         if(this.loadMask){
30862             this.maskEl = this.el;
30863         }
30864     },
30865     
30866     initEvents : function()
30867     {
30868         this.urlAPI = (window.createObjectURL && window) || 
30869                                 (window.URL && URL.revokeObjectURL && URL) || 
30870                                 (window.webkitURL && webkitURL);
30871                         
30872         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30873         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30874         
30875         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30876         this.selectorEl.hide();
30877         
30878         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30879         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30880         
30881         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30882         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30883         this.thumbEl.hide();
30884         
30885         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30886         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30887         
30888         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30889         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30890         this.errorEl.hide();
30891         
30892         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30893         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30894         this.footerEl.hide();
30895         
30896         this.setThumbBoxSize();
30897         
30898         this.bind();
30899         
30900         this.resize();
30901         
30902         this.fireEvent('initial', this);
30903     },
30904
30905     bind : function()
30906     {
30907         var _this = this;
30908         
30909         window.addEventListener("resize", function() { _this.resize(); } );
30910         
30911         this.bodyEl.on('click', this.beforeSelectFile, this);
30912         
30913         if(Roo.isTouch){
30914             this.bodyEl.on('touchstart', this.onTouchStart, this);
30915             this.bodyEl.on('touchmove', this.onTouchMove, this);
30916             this.bodyEl.on('touchend', this.onTouchEnd, this);
30917         }
30918         
30919         if(!Roo.isTouch){
30920             this.bodyEl.on('mousedown', this.onMouseDown, this);
30921             this.bodyEl.on('mousemove', this.onMouseMove, this);
30922             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30923             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30924             Roo.get(document).on('mouseup', this.onMouseUp, this);
30925         }
30926         
30927         this.selectorEl.on('change', this.onFileSelected, this);
30928     },
30929     
30930     reset : function()
30931     {    
30932         this.scale = 0;
30933         this.baseScale = 1;
30934         this.rotate = 0;
30935         this.baseRotate = 1;
30936         this.dragable = false;
30937         this.pinching = false;
30938         this.mouseX = 0;
30939         this.mouseY = 0;
30940         this.cropData = false;
30941         this.notifyEl.dom.innerHTML = this.emptyText;
30942         
30943         this.selectorEl.dom.value = '';
30944         
30945     },
30946     
30947     resize : function()
30948     {
30949         if(this.fireEvent('resize', this) != false){
30950             this.setThumbBoxPosition();
30951             this.setCanvasPosition();
30952         }
30953     },
30954     
30955     onFooterButtonClick : function(e, el, o, type)
30956     {
30957         switch (type) {
30958             case 'rotate-left' :
30959                 this.onRotateLeft(e);
30960                 break;
30961             case 'rotate-right' :
30962                 this.onRotateRight(e);
30963                 break;
30964             case 'picture' :
30965                 this.beforeSelectFile(e);
30966                 break;
30967             case 'trash' :
30968                 this.trash(e);
30969                 break;
30970             case 'crop' :
30971                 this.crop(e);
30972                 break;
30973             case 'download' :
30974                 this.download(e);
30975                 break;
30976             default :
30977                 break;
30978         }
30979         
30980         this.fireEvent('footerbuttonclick', this, type);
30981     },
30982     
30983     beforeSelectFile : function(e)
30984     {
30985         e.preventDefault();
30986         
30987         if(this.fireEvent('beforeselectfile', this) != false){
30988             this.selectorEl.dom.click();
30989         }
30990     },
30991     
30992     onFileSelected : function(e)
30993     {
30994         e.preventDefault();
30995         
30996         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30997             return;
30998         }
30999         
31000         var file = this.selectorEl.dom.files[0];
31001         
31002         if(this.fireEvent('inspect', this, file) != false){
31003             this.prepare(file);
31004         }
31005         
31006     },
31007     
31008     trash : function(e)
31009     {
31010         this.fireEvent('trash', this);
31011     },
31012     
31013     download : function(e)
31014     {
31015         this.fireEvent('download', this);
31016     },
31017     
31018     loadCanvas : function(src)
31019     {   
31020         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31021             
31022             this.reset();
31023             
31024             this.imageEl = document.createElement('img');
31025             
31026             var _this = this;
31027             
31028             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31029             
31030             this.imageEl.src = src;
31031         }
31032     },
31033     
31034     onLoadCanvas : function()
31035     {   
31036         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31037         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31038         
31039         this.bodyEl.un('click', this.beforeSelectFile, this);
31040         
31041         this.notifyEl.hide();
31042         this.thumbEl.show();
31043         this.footerEl.show();
31044         
31045         this.baseRotateLevel();
31046         
31047         if(this.isDocument){
31048             this.setThumbBoxSize();
31049         }
31050         
31051         this.setThumbBoxPosition();
31052         
31053         this.baseScaleLevel();
31054         
31055         this.draw();
31056         
31057         this.resize();
31058         
31059         this.canvasLoaded = true;
31060         
31061         if(this.loadMask){
31062             this.maskEl.unmask();
31063         }
31064         
31065     },
31066     
31067     setCanvasPosition : function()
31068     {   
31069         if(!this.canvasEl){
31070             return;
31071         }
31072         
31073         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31074         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31075         
31076         this.previewEl.setLeft(pw);
31077         this.previewEl.setTop(ph);
31078         
31079     },
31080     
31081     onMouseDown : function(e)
31082     {   
31083         e.stopEvent();
31084         
31085         this.dragable = true;
31086         this.pinching = false;
31087         
31088         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31089             this.dragable = false;
31090             return;
31091         }
31092         
31093         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31094         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31095         
31096     },
31097     
31098     onMouseMove : function(e)
31099     {   
31100         e.stopEvent();
31101         
31102         if(!this.canvasLoaded){
31103             return;
31104         }
31105         
31106         if (!this.dragable){
31107             return;
31108         }
31109         
31110         var minX = Math.ceil(this.thumbEl.getLeft(true));
31111         var minY = Math.ceil(this.thumbEl.getTop(true));
31112         
31113         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31114         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31115         
31116         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31117         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31118         
31119         x = x - this.mouseX;
31120         y = y - this.mouseY;
31121         
31122         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31123         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31124         
31125         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31126         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31127         
31128         this.previewEl.setLeft(bgX);
31129         this.previewEl.setTop(bgY);
31130         
31131         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31132         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31133     },
31134     
31135     onMouseUp : function(e)
31136     {   
31137         e.stopEvent();
31138         
31139         this.dragable = false;
31140     },
31141     
31142     onMouseWheel : function(e)
31143     {   
31144         e.stopEvent();
31145         
31146         this.startScale = this.scale;
31147         
31148         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31149         
31150         if(!this.zoomable()){
31151             this.scale = this.startScale;
31152             return;
31153         }
31154         
31155         this.draw();
31156         
31157         return;
31158     },
31159     
31160     zoomable : function()
31161     {
31162         var minScale = this.thumbEl.getWidth() / this.minWidth;
31163         
31164         if(this.minWidth < this.minHeight){
31165             minScale = this.thumbEl.getHeight() / this.minHeight;
31166         }
31167         
31168         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31169         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31170         
31171         if(
31172                 this.isDocument &&
31173                 (this.rotate == 0 || this.rotate == 180) && 
31174                 (
31175                     width > this.imageEl.OriginWidth || 
31176                     height > this.imageEl.OriginHeight ||
31177                     (width < this.minWidth && height < this.minHeight)
31178                 )
31179         ){
31180             return false;
31181         }
31182         
31183         if(
31184                 this.isDocument &&
31185                 (this.rotate == 90 || this.rotate == 270) && 
31186                 (
31187                     width > this.imageEl.OriginWidth || 
31188                     height > this.imageEl.OriginHeight ||
31189                     (width < this.minHeight && height < this.minWidth)
31190                 )
31191         ){
31192             return false;
31193         }
31194         
31195         if(
31196                 !this.isDocument &&
31197                 (this.rotate == 0 || this.rotate == 180) && 
31198                 (
31199                     width < this.minWidth || 
31200                     width > this.imageEl.OriginWidth || 
31201                     height < this.minHeight || 
31202                     height > this.imageEl.OriginHeight
31203                 )
31204         ){
31205             return false;
31206         }
31207         
31208         if(
31209                 !this.isDocument &&
31210                 (this.rotate == 90 || this.rotate == 270) && 
31211                 (
31212                     width < this.minHeight || 
31213                     width > this.imageEl.OriginWidth || 
31214                     height < this.minWidth || 
31215                     height > this.imageEl.OriginHeight
31216                 )
31217         ){
31218             return false;
31219         }
31220         
31221         return true;
31222         
31223     },
31224     
31225     onRotateLeft : function(e)
31226     {   
31227         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31228             
31229             var minScale = this.thumbEl.getWidth() / this.minWidth;
31230             
31231             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31232             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31233             
31234             this.startScale = this.scale;
31235             
31236             while (this.getScaleLevel() < minScale){
31237             
31238                 this.scale = this.scale + 1;
31239                 
31240                 if(!this.zoomable()){
31241                     break;
31242                 }
31243                 
31244                 if(
31245                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31246                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31247                 ){
31248                     continue;
31249                 }
31250                 
31251                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31252
31253                 this.draw();
31254                 
31255                 return;
31256             }
31257             
31258             this.scale = this.startScale;
31259             
31260             this.onRotateFail();
31261             
31262             return false;
31263         }
31264         
31265         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31266
31267         if(this.isDocument){
31268             this.setThumbBoxSize();
31269             this.setThumbBoxPosition();
31270             this.setCanvasPosition();
31271         }
31272         
31273         this.draw();
31274         
31275         this.fireEvent('rotate', this, 'left');
31276         
31277     },
31278     
31279     onRotateRight : function(e)
31280     {
31281         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31282             
31283             var minScale = this.thumbEl.getWidth() / this.minWidth;
31284         
31285             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31286             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31287             
31288             this.startScale = this.scale;
31289             
31290             while (this.getScaleLevel() < minScale){
31291             
31292                 this.scale = this.scale + 1;
31293                 
31294                 if(!this.zoomable()){
31295                     break;
31296                 }
31297                 
31298                 if(
31299                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31300                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31301                 ){
31302                     continue;
31303                 }
31304                 
31305                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31306
31307                 this.draw();
31308                 
31309                 return;
31310             }
31311             
31312             this.scale = this.startScale;
31313             
31314             this.onRotateFail();
31315             
31316             return false;
31317         }
31318         
31319         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31320
31321         if(this.isDocument){
31322             this.setThumbBoxSize();
31323             this.setThumbBoxPosition();
31324             this.setCanvasPosition();
31325         }
31326         
31327         this.draw();
31328         
31329         this.fireEvent('rotate', this, 'right');
31330     },
31331     
31332     onRotateFail : function()
31333     {
31334         this.errorEl.show(true);
31335         
31336         var _this = this;
31337         
31338         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31339     },
31340     
31341     draw : function()
31342     {
31343         this.previewEl.dom.innerHTML = '';
31344         
31345         var canvasEl = document.createElement("canvas");
31346         
31347         var contextEl = canvasEl.getContext("2d");
31348         
31349         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31350         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31351         var center = this.imageEl.OriginWidth / 2;
31352         
31353         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31354             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31355             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31356             center = this.imageEl.OriginHeight / 2;
31357         }
31358         
31359         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31360         
31361         contextEl.translate(center, center);
31362         contextEl.rotate(this.rotate * Math.PI / 180);
31363
31364         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31365         
31366         this.canvasEl = document.createElement("canvas");
31367         
31368         this.contextEl = this.canvasEl.getContext("2d");
31369         
31370         switch (this.rotate) {
31371             case 0 :
31372                 
31373                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31374                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31375                 
31376                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31377                 
31378                 break;
31379             case 90 : 
31380                 
31381                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31382                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31383                 
31384                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31385                     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);
31386                     break;
31387                 }
31388                 
31389                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31390                 
31391                 break;
31392             case 180 :
31393                 
31394                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31395                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31396                 
31397                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31398                     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);
31399                     break;
31400                 }
31401                 
31402                 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);
31403                 
31404                 break;
31405             case 270 :
31406                 
31407                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31408                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31409         
31410                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31411                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31412                     break;
31413                 }
31414                 
31415                 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);
31416                 
31417                 break;
31418             default : 
31419                 break;
31420         }
31421         
31422         this.previewEl.appendChild(this.canvasEl);
31423         
31424         this.setCanvasPosition();
31425     },
31426     
31427     crop : function()
31428     {
31429         if(!this.canvasLoaded){
31430             return;
31431         }
31432         
31433         var imageCanvas = document.createElement("canvas");
31434         
31435         var imageContext = imageCanvas.getContext("2d");
31436         
31437         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31438         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31439         
31440         var center = imageCanvas.width / 2;
31441         
31442         imageContext.translate(center, center);
31443         
31444         imageContext.rotate(this.rotate * Math.PI / 180);
31445         
31446         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31447         
31448         var canvas = document.createElement("canvas");
31449         
31450         var context = canvas.getContext("2d");
31451                 
31452         canvas.width = this.minWidth;
31453         canvas.height = this.minHeight;
31454
31455         switch (this.rotate) {
31456             case 0 :
31457                 
31458                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31459                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31460                 
31461                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31462                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31463                 
31464                 var targetWidth = this.minWidth - 2 * x;
31465                 var targetHeight = this.minHeight - 2 * y;
31466                 
31467                 var scale = 1;
31468                 
31469                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31470                     scale = targetWidth / width;
31471                 }
31472                 
31473                 if(x > 0 && y == 0){
31474                     scale = targetHeight / height;
31475                 }
31476                 
31477                 if(x > 0 && y > 0){
31478                     scale = targetWidth / width;
31479                     
31480                     if(width < height){
31481                         scale = targetHeight / height;
31482                     }
31483                 }
31484                 
31485                 context.scale(scale, scale);
31486                 
31487                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31488                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31489
31490                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31491                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31492
31493                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31494                 
31495                 break;
31496             case 90 : 
31497                 
31498                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31499                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31500                 
31501                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31502                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31503                 
31504                 var targetWidth = this.minWidth - 2 * x;
31505                 var targetHeight = this.minHeight - 2 * y;
31506                 
31507                 var scale = 1;
31508                 
31509                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31510                     scale = targetWidth / width;
31511                 }
31512                 
31513                 if(x > 0 && y == 0){
31514                     scale = targetHeight / height;
31515                 }
31516                 
31517                 if(x > 0 && y > 0){
31518                     scale = targetWidth / width;
31519                     
31520                     if(width < height){
31521                         scale = targetHeight / height;
31522                     }
31523                 }
31524                 
31525                 context.scale(scale, scale);
31526                 
31527                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31528                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31529
31530                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31531                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31532                 
31533                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31534                 
31535                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31536                 
31537                 break;
31538             case 180 :
31539                 
31540                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31541                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31542                 
31543                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31544                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31545                 
31546                 var targetWidth = this.minWidth - 2 * x;
31547                 var targetHeight = this.minHeight - 2 * y;
31548                 
31549                 var scale = 1;
31550                 
31551                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31552                     scale = targetWidth / width;
31553                 }
31554                 
31555                 if(x > 0 && y == 0){
31556                     scale = targetHeight / height;
31557                 }
31558                 
31559                 if(x > 0 && y > 0){
31560                     scale = targetWidth / width;
31561                     
31562                     if(width < height){
31563                         scale = targetHeight / height;
31564                     }
31565                 }
31566                 
31567                 context.scale(scale, scale);
31568                 
31569                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31570                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31571
31572                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31573                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31574
31575                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31576                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31577                 
31578                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31579                 
31580                 break;
31581             case 270 :
31582                 
31583                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31584                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31585                 
31586                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31587                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31588                 
31589                 var targetWidth = this.minWidth - 2 * x;
31590                 var targetHeight = this.minHeight - 2 * y;
31591                 
31592                 var scale = 1;
31593                 
31594                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31595                     scale = targetWidth / width;
31596                 }
31597                 
31598                 if(x > 0 && y == 0){
31599                     scale = targetHeight / height;
31600                 }
31601                 
31602                 if(x > 0 && y > 0){
31603                     scale = targetWidth / width;
31604                     
31605                     if(width < height){
31606                         scale = targetHeight / height;
31607                     }
31608                 }
31609                 
31610                 context.scale(scale, scale);
31611                 
31612                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31613                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31614
31615                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31616                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31617                 
31618                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31619                 
31620                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31621                 
31622                 break;
31623             default : 
31624                 break;
31625         }
31626         
31627         this.cropData = canvas.toDataURL(this.cropType);
31628         
31629         if(this.fireEvent('crop', this, this.cropData) !== false){
31630             this.process(this.file, this.cropData);
31631         }
31632         
31633         return;
31634         
31635     },
31636     
31637     setThumbBoxSize : function()
31638     {
31639         var width, height;
31640         
31641         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31642             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31643             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31644             
31645             this.minWidth = width;
31646             this.minHeight = height;
31647             
31648             if(this.rotate == 90 || this.rotate == 270){
31649                 this.minWidth = height;
31650                 this.minHeight = width;
31651             }
31652         }
31653         
31654         height = 300;
31655         width = Math.ceil(this.minWidth * height / this.minHeight);
31656         
31657         if(this.minWidth > this.minHeight){
31658             width = 300;
31659             height = Math.ceil(this.minHeight * width / this.minWidth);
31660         }
31661         
31662         this.thumbEl.setStyle({
31663             width : width + 'px',
31664             height : height + 'px'
31665         });
31666
31667         return;
31668             
31669     },
31670     
31671     setThumbBoxPosition : function()
31672     {
31673         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31674         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31675         
31676         this.thumbEl.setLeft(x);
31677         this.thumbEl.setTop(y);
31678         
31679     },
31680     
31681     baseRotateLevel : function()
31682     {
31683         this.baseRotate = 1;
31684         
31685         if(
31686                 typeof(this.exif) != 'undefined' &&
31687                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31688                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31689         ){
31690             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31691         }
31692         
31693         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31694         
31695     },
31696     
31697     baseScaleLevel : function()
31698     {
31699         var width, height;
31700         
31701         if(this.isDocument){
31702             
31703             if(this.baseRotate == 6 || this.baseRotate == 8){
31704             
31705                 height = this.thumbEl.getHeight();
31706                 this.baseScale = height / this.imageEl.OriginWidth;
31707
31708                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31709                     width = this.thumbEl.getWidth();
31710                     this.baseScale = width / this.imageEl.OriginHeight;
31711                 }
31712
31713                 return;
31714             }
31715
31716             height = this.thumbEl.getHeight();
31717             this.baseScale = height / this.imageEl.OriginHeight;
31718
31719             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31720                 width = this.thumbEl.getWidth();
31721                 this.baseScale = width / this.imageEl.OriginWidth;
31722             }
31723
31724             return;
31725         }
31726         
31727         if(this.baseRotate == 6 || this.baseRotate == 8){
31728             
31729             width = this.thumbEl.getHeight();
31730             this.baseScale = width / this.imageEl.OriginHeight;
31731             
31732             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31733                 height = this.thumbEl.getWidth();
31734                 this.baseScale = height / this.imageEl.OriginHeight;
31735             }
31736             
31737             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31738                 height = this.thumbEl.getWidth();
31739                 this.baseScale = height / this.imageEl.OriginHeight;
31740                 
31741                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31742                     width = this.thumbEl.getHeight();
31743                     this.baseScale = width / this.imageEl.OriginWidth;
31744                 }
31745             }
31746             
31747             return;
31748         }
31749         
31750         width = this.thumbEl.getWidth();
31751         this.baseScale = width / this.imageEl.OriginWidth;
31752         
31753         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31754             height = this.thumbEl.getHeight();
31755             this.baseScale = height / this.imageEl.OriginHeight;
31756         }
31757         
31758         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31759             
31760             height = this.thumbEl.getHeight();
31761             this.baseScale = height / this.imageEl.OriginHeight;
31762             
31763             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31764                 width = this.thumbEl.getWidth();
31765                 this.baseScale = width / this.imageEl.OriginWidth;
31766             }
31767             
31768         }
31769         
31770         return;
31771     },
31772     
31773     getScaleLevel : function()
31774     {
31775         return this.baseScale * Math.pow(1.1, this.scale);
31776     },
31777     
31778     onTouchStart : function(e)
31779     {
31780         if(!this.canvasLoaded){
31781             this.beforeSelectFile(e);
31782             return;
31783         }
31784         
31785         var touches = e.browserEvent.touches;
31786         
31787         if(!touches){
31788             return;
31789         }
31790         
31791         if(touches.length == 1){
31792             this.onMouseDown(e);
31793             return;
31794         }
31795         
31796         if(touches.length != 2){
31797             return;
31798         }
31799         
31800         var coords = [];
31801         
31802         for(var i = 0, finger; finger = touches[i]; i++){
31803             coords.push(finger.pageX, finger.pageY);
31804         }
31805         
31806         var x = Math.pow(coords[0] - coords[2], 2);
31807         var y = Math.pow(coords[1] - coords[3], 2);
31808         
31809         this.startDistance = Math.sqrt(x + y);
31810         
31811         this.startScale = this.scale;
31812         
31813         this.pinching = true;
31814         this.dragable = false;
31815         
31816     },
31817     
31818     onTouchMove : function(e)
31819     {
31820         if(!this.pinching && !this.dragable){
31821             return;
31822         }
31823         
31824         var touches = e.browserEvent.touches;
31825         
31826         if(!touches){
31827             return;
31828         }
31829         
31830         if(this.dragable){
31831             this.onMouseMove(e);
31832             return;
31833         }
31834         
31835         var coords = [];
31836         
31837         for(var i = 0, finger; finger = touches[i]; i++){
31838             coords.push(finger.pageX, finger.pageY);
31839         }
31840         
31841         var x = Math.pow(coords[0] - coords[2], 2);
31842         var y = Math.pow(coords[1] - coords[3], 2);
31843         
31844         this.endDistance = Math.sqrt(x + y);
31845         
31846         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31847         
31848         if(!this.zoomable()){
31849             this.scale = this.startScale;
31850             return;
31851         }
31852         
31853         this.draw();
31854         
31855     },
31856     
31857     onTouchEnd : function(e)
31858     {
31859         this.pinching = false;
31860         this.dragable = false;
31861         
31862     },
31863     
31864     process : function(file, crop)
31865     {
31866         if(this.loadMask){
31867             this.maskEl.mask(this.loadingText);
31868         }
31869         
31870         this.xhr = new XMLHttpRequest();
31871         
31872         file.xhr = this.xhr;
31873
31874         this.xhr.open(this.method, this.url, true);
31875         
31876         var headers = {
31877             "Accept": "application/json",
31878             "Cache-Control": "no-cache",
31879             "X-Requested-With": "XMLHttpRequest"
31880         };
31881         
31882         for (var headerName in headers) {
31883             var headerValue = headers[headerName];
31884             if (headerValue) {
31885                 this.xhr.setRequestHeader(headerName, headerValue);
31886             }
31887         }
31888         
31889         var _this = this;
31890         
31891         this.xhr.onload = function()
31892         {
31893             _this.xhrOnLoad(_this.xhr);
31894         }
31895         
31896         this.xhr.onerror = function()
31897         {
31898             _this.xhrOnError(_this.xhr);
31899         }
31900         
31901         var formData = new FormData();
31902
31903         formData.append('returnHTML', 'NO');
31904         
31905         if(crop){
31906             formData.append('crop', crop);
31907         }
31908         
31909         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31910             formData.append(this.paramName, file, file.name);
31911         }
31912         
31913         if(typeof(file.filename) != 'undefined'){
31914             formData.append('filename', file.filename);
31915         }
31916         
31917         if(typeof(file.mimetype) != 'undefined'){
31918             formData.append('mimetype', file.mimetype);
31919         }
31920         
31921         if(this.fireEvent('arrange', this, formData) != false){
31922             this.xhr.send(formData);
31923         };
31924     },
31925     
31926     xhrOnLoad : function(xhr)
31927     {
31928         if(this.loadMask){
31929             this.maskEl.unmask();
31930         }
31931         
31932         if (xhr.readyState !== 4) {
31933             this.fireEvent('exception', this, xhr);
31934             return;
31935         }
31936
31937         var response = Roo.decode(xhr.responseText);
31938         
31939         if(!response.success){
31940             this.fireEvent('exception', this, xhr);
31941             return;
31942         }
31943         
31944         var response = Roo.decode(xhr.responseText);
31945         
31946         this.fireEvent('upload', this, response);
31947         
31948     },
31949     
31950     xhrOnError : function()
31951     {
31952         if(this.loadMask){
31953             this.maskEl.unmask();
31954         }
31955         
31956         Roo.log('xhr on error');
31957         
31958         var response = Roo.decode(xhr.responseText);
31959           
31960         Roo.log(response);
31961         
31962     },
31963     
31964     prepare : function(file)
31965     {   
31966         if(this.loadMask){
31967             this.maskEl.mask(this.loadingText);
31968         }
31969         
31970         this.file = false;
31971         this.exif = {};
31972         
31973         if(typeof(file) === 'string'){
31974             this.loadCanvas(file);
31975             return;
31976         }
31977         
31978         if(!file || !this.urlAPI){
31979             return;
31980         }
31981         
31982         this.file = file;
31983         this.cropType = file.type;
31984         
31985         var _this = this;
31986         
31987         if(this.fireEvent('prepare', this, this.file) != false){
31988             
31989             var reader = new FileReader();
31990             
31991             reader.onload = function (e) {
31992                 if (e.target.error) {
31993                     Roo.log(e.target.error);
31994                     return;
31995                 }
31996                 
31997                 var buffer = e.target.result,
31998                     dataView = new DataView(buffer),
31999                     offset = 2,
32000                     maxOffset = dataView.byteLength - 4,
32001                     markerBytes,
32002                     markerLength;
32003                 
32004                 if (dataView.getUint16(0) === 0xffd8) {
32005                     while (offset < maxOffset) {
32006                         markerBytes = dataView.getUint16(offset);
32007                         
32008                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32009                             markerLength = dataView.getUint16(offset + 2) + 2;
32010                             if (offset + markerLength > dataView.byteLength) {
32011                                 Roo.log('Invalid meta data: Invalid segment size.');
32012                                 break;
32013                             }
32014                             
32015                             if(markerBytes == 0xffe1){
32016                                 _this.parseExifData(
32017                                     dataView,
32018                                     offset,
32019                                     markerLength
32020                                 );
32021                             }
32022                             
32023                             offset += markerLength;
32024                             
32025                             continue;
32026                         }
32027                         
32028                         break;
32029                     }
32030                     
32031                 }
32032                 
32033                 var url = _this.urlAPI.createObjectURL(_this.file);
32034                 
32035                 _this.loadCanvas(url);
32036                 
32037                 return;
32038             }
32039             
32040             reader.readAsArrayBuffer(this.file);
32041             
32042         }
32043         
32044     },
32045     
32046     parseExifData : function(dataView, offset, length)
32047     {
32048         var tiffOffset = offset + 10,
32049             littleEndian,
32050             dirOffset;
32051     
32052         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32053             // No Exif data, might be XMP data instead
32054             return;
32055         }
32056         
32057         // Check for the ASCII code for "Exif" (0x45786966):
32058         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32059             // No Exif data, might be XMP data instead
32060             return;
32061         }
32062         if (tiffOffset + 8 > dataView.byteLength) {
32063             Roo.log('Invalid Exif data: Invalid segment size.');
32064             return;
32065         }
32066         // Check for the two null bytes:
32067         if (dataView.getUint16(offset + 8) !== 0x0000) {
32068             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32069             return;
32070         }
32071         // Check the byte alignment:
32072         switch (dataView.getUint16(tiffOffset)) {
32073         case 0x4949:
32074             littleEndian = true;
32075             break;
32076         case 0x4D4D:
32077             littleEndian = false;
32078             break;
32079         default:
32080             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32081             return;
32082         }
32083         // Check for the TIFF tag marker (0x002A):
32084         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32085             Roo.log('Invalid Exif data: Missing TIFF marker.');
32086             return;
32087         }
32088         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32089         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32090         
32091         this.parseExifTags(
32092             dataView,
32093             tiffOffset,
32094             tiffOffset + dirOffset,
32095             littleEndian
32096         );
32097     },
32098     
32099     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32100     {
32101         var tagsNumber,
32102             dirEndOffset,
32103             i;
32104         if (dirOffset + 6 > dataView.byteLength) {
32105             Roo.log('Invalid Exif data: Invalid directory offset.');
32106             return;
32107         }
32108         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32109         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32110         if (dirEndOffset + 4 > dataView.byteLength) {
32111             Roo.log('Invalid Exif data: Invalid directory size.');
32112             return;
32113         }
32114         for (i = 0; i < tagsNumber; i += 1) {
32115             this.parseExifTag(
32116                 dataView,
32117                 tiffOffset,
32118                 dirOffset + 2 + 12 * i, // tag offset
32119                 littleEndian
32120             );
32121         }
32122         // Return the offset to the next directory:
32123         return dataView.getUint32(dirEndOffset, littleEndian);
32124     },
32125     
32126     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32127     {
32128         var tag = dataView.getUint16(offset, littleEndian);
32129         
32130         this.exif[tag] = this.getExifValue(
32131             dataView,
32132             tiffOffset,
32133             offset,
32134             dataView.getUint16(offset + 2, littleEndian), // tag type
32135             dataView.getUint32(offset + 4, littleEndian), // tag length
32136             littleEndian
32137         );
32138     },
32139     
32140     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32141     {
32142         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32143             tagSize,
32144             dataOffset,
32145             values,
32146             i,
32147             str,
32148             c;
32149     
32150         if (!tagType) {
32151             Roo.log('Invalid Exif data: Invalid tag type.');
32152             return;
32153         }
32154         
32155         tagSize = tagType.size * length;
32156         // Determine if the value is contained in the dataOffset bytes,
32157         // or if the value at the dataOffset is a pointer to the actual data:
32158         dataOffset = tagSize > 4 ?
32159                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32160         if (dataOffset + tagSize > dataView.byteLength) {
32161             Roo.log('Invalid Exif data: Invalid data offset.');
32162             return;
32163         }
32164         if (length === 1) {
32165             return tagType.getValue(dataView, dataOffset, littleEndian);
32166         }
32167         values = [];
32168         for (i = 0; i < length; i += 1) {
32169             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32170         }
32171         
32172         if (tagType.ascii) {
32173             str = '';
32174             // Concatenate the chars:
32175             for (i = 0; i < values.length; i += 1) {
32176                 c = values[i];
32177                 // Ignore the terminating NULL byte(s):
32178                 if (c === '\u0000') {
32179                     break;
32180                 }
32181                 str += c;
32182             }
32183             return str;
32184         }
32185         return values;
32186     }
32187     
32188 });
32189
32190 Roo.apply(Roo.bootstrap.UploadCropbox, {
32191     tags : {
32192         'Orientation': 0x0112
32193     },
32194     
32195     Orientation: {
32196             1: 0, //'top-left',
32197 //            2: 'top-right',
32198             3: 180, //'bottom-right',
32199 //            4: 'bottom-left',
32200 //            5: 'left-top',
32201             6: 90, //'right-top',
32202 //            7: 'right-bottom',
32203             8: 270 //'left-bottom'
32204     },
32205     
32206     exifTagTypes : {
32207         // byte, 8-bit unsigned int:
32208         1: {
32209             getValue: function (dataView, dataOffset) {
32210                 return dataView.getUint8(dataOffset);
32211             },
32212             size: 1
32213         },
32214         // ascii, 8-bit byte:
32215         2: {
32216             getValue: function (dataView, dataOffset) {
32217                 return String.fromCharCode(dataView.getUint8(dataOffset));
32218             },
32219             size: 1,
32220             ascii: true
32221         },
32222         // short, 16 bit int:
32223         3: {
32224             getValue: function (dataView, dataOffset, littleEndian) {
32225                 return dataView.getUint16(dataOffset, littleEndian);
32226             },
32227             size: 2
32228         },
32229         // long, 32 bit int:
32230         4: {
32231             getValue: function (dataView, dataOffset, littleEndian) {
32232                 return dataView.getUint32(dataOffset, littleEndian);
32233             },
32234             size: 4
32235         },
32236         // rational = two long values, first is numerator, second is denominator:
32237         5: {
32238             getValue: function (dataView, dataOffset, littleEndian) {
32239                 return dataView.getUint32(dataOffset, littleEndian) /
32240                     dataView.getUint32(dataOffset + 4, littleEndian);
32241             },
32242             size: 8
32243         },
32244         // slong, 32 bit signed int:
32245         9: {
32246             getValue: function (dataView, dataOffset, littleEndian) {
32247                 return dataView.getInt32(dataOffset, littleEndian);
32248             },
32249             size: 4
32250         },
32251         // srational, two slongs, first is numerator, second is denominator:
32252         10: {
32253             getValue: function (dataView, dataOffset, littleEndian) {
32254                 return dataView.getInt32(dataOffset, littleEndian) /
32255                     dataView.getInt32(dataOffset + 4, littleEndian);
32256             },
32257             size: 8
32258         }
32259     },
32260     
32261     footer : {
32262         STANDARD : [
32263             {
32264                 tag : 'div',
32265                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32266                 action : 'rotate-left',
32267                 cn : [
32268                     {
32269                         tag : 'button',
32270                         cls : 'btn btn-default',
32271                         html : '<i class="fa fa-undo"></i>'
32272                     }
32273                 ]
32274             },
32275             {
32276                 tag : 'div',
32277                 cls : 'btn-group roo-upload-cropbox-picture',
32278                 action : 'picture',
32279                 cn : [
32280                     {
32281                         tag : 'button',
32282                         cls : 'btn btn-default',
32283                         html : '<i class="fa fa-picture-o"></i>'
32284                     }
32285                 ]
32286             },
32287             {
32288                 tag : 'div',
32289                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32290                 action : 'rotate-right',
32291                 cn : [
32292                     {
32293                         tag : 'button',
32294                         cls : 'btn btn-default',
32295                         html : '<i class="fa fa-repeat"></i>'
32296                     }
32297                 ]
32298             }
32299         ],
32300         DOCUMENT : [
32301             {
32302                 tag : 'div',
32303                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32304                 action : 'rotate-left',
32305                 cn : [
32306                     {
32307                         tag : 'button',
32308                         cls : 'btn btn-default',
32309                         html : '<i class="fa fa-undo"></i>'
32310                     }
32311                 ]
32312             },
32313             {
32314                 tag : 'div',
32315                 cls : 'btn-group roo-upload-cropbox-download',
32316                 action : 'download',
32317                 cn : [
32318                     {
32319                         tag : 'button',
32320                         cls : 'btn btn-default',
32321                         html : '<i class="fa fa-download"></i>'
32322                     }
32323                 ]
32324             },
32325             {
32326                 tag : 'div',
32327                 cls : 'btn-group roo-upload-cropbox-crop',
32328                 action : 'crop',
32329                 cn : [
32330                     {
32331                         tag : 'button',
32332                         cls : 'btn btn-default',
32333                         html : '<i class="fa fa-crop"></i>'
32334                     }
32335                 ]
32336             },
32337             {
32338                 tag : 'div',
32339                 cls : 'btn-group roo-upload-cropbox-trash',
32340                 action : 'trash',
32341                 cn : [
32342                     {
32343                         tag : 'button',
32344                         cls : 'btn btn-default',
32345                         html : '<i class="fa fa-trash"></i>'
32346                     }
32347                 ]
32348             },
32349             {
32350                 tag : 'div',
32351                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32352                 action : 'rotate-right',
32353                 cn : [
32354                     {
32355                         tag : 'button',
32356                         cls : 'btn btn-default',
32357                         html : '<i class="fa fa-repeat"></i>'
32358                     }
32359                 ]
32360             }
32361         ],
32362         ROTATOR : [
32363             {
32364                 tag : 'div',
32365                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32366                 action : 'rotate-left',
32367                 cn : [
32368                     {
32369                         tag : 'button',
32370                         cls : 'btn btn-default',
32371                         html : '<i class="fa fa-undo"></i>'
32372                     }
32373                 ]
32374             },
32375             {
32376                 tag : 'div',
32377                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32378                 action : 'rotate-right',
32379                 cn : [
32380                     {
32381                         tag : 'button',
32382                         cls : 'btn btn-default',
32383                         html : '<i class="fa fa-repeat"></i>'
32384                     }
32385                 ]
32386             }
32387         ]
32388     }
32389 });
32390
32391 /*
32392 * Licence: LGPL
32393 */
32394
32395 /**
32396  * @class Roo.bootstrap.DocumentManager
32397  * @extends Roo.bootstrap.Component
32398  * Bootstrap DocumentManager class
32399  * @cfg {String} paramName default 'imageUpload'
32400  * @cfg {String} toolTipName default 'filename'
32401  * @cfg {String} method default POST
32402  * @cfg {String} url action url
32403  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32404  * @cfg {Boolean} multiple multiple upload default true
32405  * @cfg {Number} thumbSize default 300
32406  * @cfg {String} fieldLabel
32407  * @cfg {Number} labelWidth default 4
32408  * @cfg {String} labelAlign (left|top) default left
32409  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32410 * @cfg {Number} labellg set the width of label (1-12)
32411  * @cfg {Number} labelmd set the width of label (1-12)
32412  * @cfg {Number} labelsm set the width of label (1-12)
32413  * @cfg {Number} labelxs set the width of label (1-12)
32414  * 
32415  * @constructor
32416  * Create a new DocumentManager
32417  * @param {Object} config The config object
32418  */
32419
32420 Roo.bootstrap.DocumentManager = function(config){
32421     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32422     
32423     this.files = [];
32424     this.delegates = [];
32425     
32426     this.addEvents({
32427         /**
32428          * @event initial
32429          * Fire when initial the DocumentManager
32430          * @param {Roo.bootstrap.DocumentManager} this
32431          */
32432         "initial" : true,
32433         /**
32434          * @event inspect
32435          * inspect selected file
32436          * @param {Roo.bootstrap.DocumentManager} this
32437          * @param {File} file
32438          */
32439         "inspect" : true,
32440         /**
32441          * @event exception
32442          * Fire when xhr load exception
32443          * @param {Roo.bootstrap.DocumentManager} this
32444          * @param {XMLHttpRequest} xhr
32445          */
32446         "exception" : true,
32447         /**
32448          * @event afterupload
32449          * Fire when xhr load exception
32450          * @param {Roo.bootstrap.DocumentManager} this
32451          * @param {XMLHttpRequest} xhr
32452          */
32453         "afterupload" : true,
32454         /**
32455          * @event prepare
32456          * prepare the form data
32457          * @param {Roo.bootstrap.DocumentManager} this
32458          * @param {Object} formData
32459          */
32460         "prepare" : true,
32461         /**
32462          * @event remove
32463          * Fire when remove the file
32464          * @param {Roo.bootstrap.DocumentManager} this
32465          * @param {Object} file
32466          */
32467         "remove" : true,
32468         /**
32469          * @event refresh
32470          * Fire after refresh the file
32471          * @param {Roo.bootstrap.DocumentManager} this
32472          */
32473         "refresh" : true,
32474         /**
32475          * @event click
32476          * Fire after click the image
32477          * @param {Roo.bootstrap.DocumentManager} this
32478          * @param {Object} file
32479          */
32480         "click" : true,
32481         /**
32482          * @event edit
32483          * Fire when upload a image and editable set to true
32484          * @param {Roo.bootstrap.DocumentManager} this
32485          * @param {Object} file
32486          */
32487         "edit" : true,
32488         /**
32489          * @event beforeselectfile
32490          * Fire before select file
32491          * @param {Roo.bootstrap.DocumentManager} this
32492          */
32493         "beforeselectfile" : true,
32494         /**
32495          * @event process
32496          * Fire before process file
32497          * @param {Roo.bootstrap.DocumentManager} this
32498          * @param {Object} file
32499          */
32500         "process" : true,
32501         /**
32502          * @event previewrendered
32503          * Fire when preview rendered
32504          * @param {Roo.bootstrap.DocumentManager} this
32505          * @param {Object} file
32506          */
32507         "previewrendered" : true,
32508         /**
32509          */
32510         "previewResize" : true
32511         
32512     });
32513 };
32514
32515 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32516     
32517     boxes : 0,
32518     inputName : '',
32519     thumbSize : 300,
32520     multiple : true,
32521     files : false,
32522     method : 'POST',
32523     url : '',
32524     paramName : 'imageUpload',
32525     toolTipName : 'filename',
32526     fieldLabel : '',
32527     labelWidth : 4,
32528     labelAlign : 'left',
32529     editable : true,
32530     delegates : false,
32531     xhr : false, 
32532     
32533     labellg : 0,
32534     labelmd : 0,
32535     labelsm : 0,
32536     labelxs : 0,
32537     
32538     getAutoCreate : function()
32539     {   
32540         var managerWidget = {
32541             tag : 'div',
32542             cls : 'roo-document-manager',
32543             cn : [
32544                 {
32545                     tag : 'input',
32546                     cls : 'roo-document-manager-selector',
32547                     type : 'file'
32548                 },
32549                 {
32550                     tag : 'div',
32551                     cls : 'roo-document-manager-uploader',
32552                     cn : [
32553                         {
32554                             tag : 'div',
32555                             cls : 'roo-document-manager-upload-btn',
32556                             html : '<i class="fa fa-plus"></i>'
32557                         }
32558                     ]
32559                     
32560                 }
32561             ]
32562         };
32563         
32564         var content = [
32565             {
32566                 tag : 'div',
32567                 cls : 'column col-md-12',
32568                 cn : managerWidget
32569             }
32570         ];
32571         
32572         if(this.fieldLabel.length){
32573             
32574             content = [
32575                 {
32576                     tag : 'div',
32577                     cls : 'column col-md-12',
32578                     html : this.fieldLabel
32579                 },
32580                 {
32581                     tag : 'div',
32582                     cls : 'column col-md-12',
32583                     cn : managerWidget
32584                 }
32585             ];
32586
32587             if(this.labelAlign == 'left'){
32588                 content = [
32589                     {
32590                         tag : 'div',
32591                         cls : 'column',
32592                         html : this.fieldLabel
32593                     },
32594                     {
32595                         tag : 'div',
32596                         cls : 'column',
32597                         cn : managerWidget
32598                     }
32599                 ];
32600                 
32601                 if(this.labelWidth > 12){
32602                     content[0].style = "width: " + this.labelWidth + 'px';
32603                 }
32604
32605                 if(this.labelWidth < 13 && this.labelmd == 0){
32606                     this.labelmd = this.labelWidth;
32607                 }
32608
32609                 if(this.labellg > 0){
32610                     content[0].cls += ' col-lg-' + this.labellg;
32611                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32612                 }
32613
32614                 if(this.labelmd > 0){
32615                     content[0].cls += ' col-md-' + this.labelmd;
32616                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32617                 }
32618
32619                 if(this.labelsm > 0){
32620                     content[0].cls += ' col-sm-' + this.labelsm;
32621                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32622                 }
32623
32624                 if(this.labelxs > 0){
32625                     content[0].cls += ' col-xs-' + this.labelxs;
32626                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32627                 }
32628                 
32629             }
32630         }
32631         
32632         var cfg = {
32633             tag : 'div',
32634             cls : 'row clearfix',
32635             cn : content
32636         };
32637         
32638         return cfg;
32639         
32640     },
32641     
32642     initEvents : function()
32643     {
32644         this.managerEl = this.el.select('.roo-document-manager', true).first();
32645         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32646         
32647         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32648         this.selectorEl.hide();
32649         
32650         if(this.multiple){
32651             this.selectorEl.attr('multiple', 'multiple');
32652         }
32653         
32654         this.selectorEl.on('change', this.onFileSelected, this);
32655         
32656         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32657         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32658         
32659         this.uploader.on('click', this.onUploaderClick, this);
32660         
32661         this.renderProgressDialog();
32662         
32663         var _this = this;
32664         
32665         window.addEventListener("resize", function() { _this.refresh(); } );
32666         
32667         this.fireEvent('initial', this);
32668     },
32669     
32670     renderProgressDialog : function()
32671     {
32672         var _this = this;
32673         
32674         this.progressDialog = new Roo.bootstrap.Modal({
32675             cls : 'roo-document-manager-progress-dialog',
32676             allow_close : false,
32677             animate : false,
32678             title : '',
32679             buttons : [
32680                 {
32681                     name  :'cancel',
32682                     weight : 'danger',
32683                     html : 'Cancel'
32684                 }
32685             ], 
32686             listeners : { 
32687                 btnclick : function() {
32688                     _this.uploadCancel();
32689                     this.hide();
32690                 }
32691             }
32692         });
32693          
32694         this.progressDialog.render(Roo.get(document.body));
32695          
32696         this.progress = new Roo.bootstrap.Progress({
32697             cls : 'roo-document-manager-progress',
32698             active : true,
32699             striped : true
32700         });
32701         
32702         this.progress.render(this.progressDialog.getChildContainer());
32703         
32704         this.progressBar = new Roo.bootstrap.ProgressBar({
32705             cls : 'roo-document-manager-progress-bar',
32706             aria_valuenow : 0,
32707             aria_valuemin : 0,
32708             aria_valuemax : 12,
32709             panel : 'success'
32710         });
32711         
32712         this.progressBar.render(this.progress.getChildContainer());
32713     },
32714     
32715     onUploaderClick : function(e)
32716     {
32717         e.preventDefault();
32718      
32719         if(this.fireEvent('beforeselectfile', this) != false){
32720             this.selectorEl.dom.click();
32721         }
32722         
32723     },
32724     
32725     onFileSelected : function(e)
32726     {
32727         e.preventDefault();
32728         
32729         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32730             return;
32731         }
32732         
32733         Roo.each(this.selectorEl.dom.files, function(file){
32734             if(this.fireEvent('inspect', this, file) != false){
32735                 this.files.push(file);
32736             }
32737         }, this);
32738         
32739         this.queue();
32740         
32741     },
32742     
32743     queue : function()
32744     {
32745         this.selectorEl.dom.value = '';
32746         
32747         if(!this.files || !this.files.length){
32748             return;
32749         }
32750         
32751         if(this.boxes > 0 && this.files.length > this.boxes){
32752             this.files = this.files.slice(0, this.boxes);
32753         }
32754         
32755         this.uploader.show();
32756         
32757         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32758             this.uploader.hide();
32759         }
32760         
32761         var _this = this;
32762         
32763         var files = [];
32764         
32765         var docs = [];
32766         
32767         Roo.each(this.files, function(file){
32768             
32769             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32770                 var f = this.renderPreview(file);
32771                 files.push(f);
32772                 return;
32773             }
32774             
32775             if(file.type.indexOf('image') != -1){
32776                 this.delegates.push(
32777                     (function(){
32778                         _this.process(file);
32779                     }).createDelegate(this)
32780                 );
32781         
32782                 return;
32783             }
32784             
32785             docs.push(
32786                 (function(){
32787                     _this.process(file);
32788                 }).createDelegate(this)
32789             );
32790             
32791         }, this);
32792         
32793         this.files = files;
32794         
32795         this.delegates = this.delegates.concat(docs);
32796         
32797         if(!this.delegates.length){
32798             this.refresh();
32799             return;
32800         }
32801         
32802         this.progressBar.aria_valuemax = this.delegates.length;
32803         
32804         this.arrange();
32805         
32806         return;
32807     },
32808     
32809     arrange : function()
32810     {
32811         if(!this.delegates.length){
32812             this.progressDialog.hide();
32813             this.refresh();
32814             return;
32815         }
32816         
32817         var delegate = this.delegates.shift();
32818         
32819         this.progressDialog.show();
32820         
32821         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32822         
32823         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32824         
32825         delegate();
32826     },
32827     
32828     refresh : function()
32829     {
32830         this.uploader.show();
32831         
32832         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32833             this.uploader.hide();
32834         }
32835         
32836         Roo.isTouch ? this.closable(false) : this.closable(true);
32837         
32838         this.fireEvent('refresh', this);
32839     },
32840     
32841     onRemove : function(e, el, o)
32842     {
32843         e.preventDefault();
32844         
32845         this.fireEvent('remove', this, o);
32846         
32847     },
32848     
32849     remove : function(o)
32850     {
32851         var files = [];
32852         
32853         Roo.each(this.files, function(file){
32854             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32855                 files.push(file);
32856                 return;
32857             }
32858
32859             o.target.remove();
32860
32861         }, this);
32862         
32863         this.files = files;
32864         
32865         this.refresh();
32866     },
32867     
32868     clear : function()
32869     {
32870         Roo.each(this.files, function(file){
32871             if(!file.target){
32872                 return;
32873             }
32874             
32875             file.target.remove();
32876
32877         }, this);
32878         
32879         this.files = [];
32880         
32881         this.refresh();
32882     },
32883     
32884     onClick : function(e, el, o)
32885     {
32886         e.preventDefault();
32887         
32888         this.fireEvent('click', this, o);
32889         
32890     },
32891     
32892     closable : function(closable)
32893     {
32894         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32895             
32896             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32897             
32898             if(closable){
32899                 el.show();
32900                 return;
32901             }
32902             
32903             el.hide();
32904             
32905         }, this);
32906     },
32907     
32908     xhrOnLoad : function(xhr)
32909     {
32910         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32911             el.remove();
32912         }, this);
32913         
32914         if (xhr.readyState !== 4) {
32915             this.arrange();
32916             this.fireEvent('exception', this, xhr);
32917             return;
32918         }
32919
32920         var response = Roo.decode(xhr.responseText);
32921         
32922         if(!response.success){
32923             this.arrange();
32924             this.fireEvent('exception', this, xhr);
32925             return;
32926         }
32927         
32928         var file = this.renderPreview(response.data);
32929         
32930         this.files.push(file);
32931         
32932         this.arrange();
32933         
32934         this.fireEvent('afterupload', this, xhr);
32935         
32936     },
32937     
32938     xhrOnError : function(xhr)
32939     {
32940         Roo.log('xhr on error');
32941         
32942         var response = Roo.decode(xhr.responseText);
32943           
32944         Roo.log(response);
32945         
32946         this.arrange();
32947     },
32948     
32949     process : function(file)
32950     {
32951         if(this.fireEvent('process', this, file) !== false){
32952             if(this.editable && file.type.indexOf('image') != -1){
32953                 this.fireEvent('edit', this, file);
32954                 return;
32955             }
32956
32957             this.uploadStart(file, false);
32958
32959             return;
32960         }
32961         
32962     },
32963     
32964     uploadStart : function(file, crop)
32965     {
32966         this.xhr = new XMLHttpRequest();
32967         
32968         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32969             this.arrange();
32970             return;
32971         }
32972         
32973         file.xhr = this.xhr;
32974             
32975         this.managerEl.createChild({
32976             tag : 'div',
32977             cls : 'roo-document-manager-loading',
32978             cn : [
32979                 {
32980                     tag : 'div',
32981                     tooltip : file.name,
32982                     cls : 'roo-document-manager-thumb',
32983                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32984                 }
32985             ]
32986
32987         });
32988
32989         this.xhr.open(this.method, this.url, true);
32990         
32991         var headers = {
32992             "Accept": "application/json",
32993             "Cache-Control": "no-cache",
32994             "X-Requested-With": "XMLHttpRequest"
32995         };
32996         
32997         for (var headerName in headers) {
32998             var headerValue = headers[headerName];
32999             if (headerValue) {
33000                 this.xhr.setRequestHeader(headerName, headerValue);
33001             }
33002         }
33003         
33004         var _this = this;
33005         
33006         this.xhr.onload = function()
33007         {
33008             _this.xhrOnLoad(_this.xhr);
33009         }
33010         
33011         this.xhr.onerror = function()
33012         {
33013             _this.xhrOnError(_this.xhr);
33014         }
33015         
33016         var formData = new FormData();
33017
33018         formData.append('returnHTML', 'NO');
33019         
33020         if(crop){
33021             formData.append('crop', crop);
33022         }
33023         
33024         formData.append(this.paramName, file, file.name);
33025         
33026         var options = {
33027             file : file, 
33028             manually : false
33029         };
33030         
33031         if(this.fireEvent('prepare', this, formData, options) != false){
33032             
33033             if(options.manually){
33034                 return;
33035             }
33036             
33037             this.xhr.send(formData);
33038             return;
33039         };
33040         
33041         this.uploadCancel();
33042     },
33043     
33044     uploadCancel : function()
33045     {
33046         if (this.xhr) {
33047             this.xhr.abort();
33048         }
33049         
33050         this.delegates = [];
33051         
33052         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33053             el.remove();
33054         }, this);
33055         
33056         this.arrange();
33057     },
33058     
33059     renderPreview : function(file)
33060     {
33061         if(typeof(file.target) != 'undefined' && file.target){
33062             return file;
33063         }
33064         
33065         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33066         
33067         var previewEl = this.managerEl.createChild({
33068             tag : 'div',
33069             cls : 'roo-document-manager-preview',
33070             cn : [
33071                 {
33072                     tag : 'div',
33073                     tooltip : file[this.toolTipName],
33074                     cls : 'roo-document-manager-thumb',
33075                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33076                 },
33077                 {
33078                     tag : 'button',
33079                     cls : 'close',
33080                     html : '<i class="fa fa-times-circle"></i>'
33081                 }
33082             ]
33083         });
33084
33085         var close = previewEl.select('button.close', true).first();
33086
33087         close.on('click', this.onRemove, this, file);
33088
33089         file.target = previewEl;
33090
33091         var image = previewEl.select('img', true).first();
33092         
33093         var _this = this;
33094         
33095         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33096         
33097         image.on('click', this.onClick, this, file);
33098         
33099         this.fireEvent('previewrendered', this, file);
33100         
33101         return file;
33102         
33103     },
33104     
33105     onPreviewLoad : function(file, image)
33106     {
33107         if(typeof(file.target) == 'undefined' || !file.target){
33108             return;
33109         }
33110         
33111         var width = image.dom.naturalWidth || image.dom.width;
33112         var height = image.dom.naturalHeight || image.dom.height;
33113         
33114         if(!this.previewResize) {
33115             return;
33116         }
33117         
33118         if(width > height){
33119             file.target.addClass('wide');
33120             return;
33121         }
33122         
33123         file.target.addClass('tall');
33124         return;
33125         
33126     },
33127     
33128     uploadFromSource : function(file, crop)
33129     {
33130         this.xhr = new XMLHttpRequest();
33131         
33132         this.managerEl.createChild({
33133             tag : 'div',
33134             cls : 'roo-document-manager-loading',
33135             cn : [
33136                 {
33137                     tag : 'div',
33138                     tooltip : file.name,
33139                     cls : 'roo-document-manager-thumb',
33140                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33141                 }
33142             ]
33143
33144         });
33145
33146         this.xhr.open(this.method, this.url, true);
33147         
33148         var headers = {
33149             "Accept": "application/json",
33150             "Cache-Control": "no-cache",
33151             "X-Requested-With": "XMLHttpRequest"
33152         };
33153         
33154         for (var headerName in headers) {
33155             var headerValue = headers[headerName];
33156             if (headerValue) {
33157                 this.xhr.setRequestHeader(headerName, headerValue);
33158             }
33159         }
33160         
33161         var _this = this;
33162         
33163         this.xhr.onload = function()
33164         {
33165             _this.xhrOnLoad(_this.xhr);
33166         }
33167         
33168         this.xhr.onerror = function()
33169         {
33170             _this.xhrOnError(_this.xhr);
33171         }
33172         
33173         var formData = new FormData();
33174
33175         formData.append('returnHTML', 'NO');
33176         
33177         formData.append('crop', crop);
33178         
33179         if(typeof(file.filename) != 'undefined'){
33180             formData.append('filename', file.filename);
33181         }
33182         
33183         if(typeof(file.mimetype) != 'undefined'){
33184             formData.append('mimetype', file.mimetype);
33185         }
33186         
33187         Roo.log(formData);
33188         
33189         if(this.fireEvent('prepare', this, formData) != false){
33190             this.xhr.send(formData);
33191         };
33192     }
33193 });
33194
33195 /*
33196 * Licence: LGPL
33197 */
33198
33199 /**
33200  * @class Roo.bootstrap.DocumentViewer
33201  * @extends Roo.bootstrap.Component
33202  * Bootstrap DocumentViewer class
33203  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33204  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33205  * 
33206  * @constructor
33207  * Create a new DocumentViewer
33208  * @param {Object} config The config object
33209  */
33210
33211 Roo.bootstrap.DocumentViewer = function(config){
33212     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33213     
33214     this.addEvents({
33215         /**
33216          * @event initial
33217          * Fire after initEvent
33218          * @param {Roo.bootstrap.DocumentViewer} this
33219          */
33220         "initial" : true,
33221         /**
33222          * @event click
33223          * Fire after click
33224          * @param {Roo.bootstrap.DocumentViewer} this
33225          */
33226         "click" : true,
33227         /**
33228          * @event download
33229          * Fire after download button
33230          * @param {Roo.bootstrap.DocumentViewer} this
33231          */
33232         "download" : true,
33233         /**
33234          * @event trash
33235          * Fire after trash button
33236          * @param {Roo.bootstrap.DocumentViewer} this
33237          */
33238         "trash" : true
33239         
33240     });
33241 };
33242
33243 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33244     
33245     showDownload : true,
33246     
33247     showTrash : true,
33248     
33249     getAutoCreate : function()
33250     {
33251         var cfg = {
33252             tag : 'div',
33253             cls : 'roo-document-viewer',
33254             cn : [
33255                 {
33256                     tag : 'div',
33257                     cls : 'roo-document-viewer-body',
33258                     cn : [
33259                         {
33260                             tag : 'div',
33261                             cls : 'roo-document-viewer-thumb',
33262                             cn : [
33263                                 {
33264                                     tag : 'img',
33265                                     cls : 'roo-document-viewer-image'
33266                                 }
33267                             ]
33268                         }
33269                     ]
33270                 },
33271                 {
33272                     tag : 'div',
33273                     cls : 'roo-document-viewer-footer',
33274                     cn : {
33275                         tag : 'div',
33276                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33277                         cn : [
33278                             {
33279                                 tag : 'div',
33280                                 cls : 'btn-group roo-document-viewer-download',
33281                                 cn : [
33282                                     {
33283                                         tag : 'button',
33284                                         cls : 'btn btn-default',
33285                                         html : '<i class="fa fa-download"></i>'
33286                                     }
33287                                 ]
33288                             },
33289                             {
33290                                 tag : 'div',
33291                                 cls : 'btn-group roo-document-viewer-trash',
33292                                 cn : [
33293                                     {
33294                                         tag : 'button',
33295                                         cls : 'btn btn-default',
33296                                         html : '<i class="fa fa-trash"></i>'
33297                                     }
33298                                 ]
33299                             }
33300                         ]
33301                     }
33302                 }
33303             ]
33304         };
33305         
33306         return cfg;
33307     },
33308     
33309     initEvents : function()
33310     {
33311         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33312         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33313         
33314         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33315         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33316         
33317         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33318         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33319         
33320         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33321         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33322         
33323         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33324         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33325         
33326         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33327         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33328         
33329         this.bodyEl.on('click', this.onClick, this);
33330         this.downloadBtn.on('click', this.onDownload, this);
33331         this.trashBtn.on('click', this.onTrash, this);
33332         
33333         this.downloadBtn.hide();
33334         this.trashBtn.hide();
33335         
33336         if(this.showDownload){
33337             this.downloadBtn.show();
33338         }
33339         
33340         if(this.showTrash){
33341             this.trashBtn.show();
33342         }
33343         
33344         if(!this.showDownload && !this.showTrash) {
33345             this.footerEl.hide();
33346         }
33347         
33348     },
33349     
33350     initial : function()
33351     {
33352         this.fireEvent('initial', this);
33353         
33354     },
33355     
33356     onClick : function(e)
33357     {
33358         e.preventDefault();
33359         
33360         this.fireEvent('click', this);
33361     },
33362     
33363     onDownload : function(e)
33364     {
33365         e.preventDefault();
33366         
33367         this.fireEvent('download', this);
33368     },
33369     
33370     onTrash : function(e)
33371     {
33372         e.preventDefault();
33373         
33374         this.fireEvent('trash', this);
33375     }
33376     
33377 });
33378 /*
33379  * - LGPL
33380  *
33381  * FieldLabel
33382  * 
33383  */
33384
33385 /**
33386  * @class Roo.bootstrap.form.FieldLabel
33387  * @extends Roo.bootstrap.Component
33388  * Bootstrap FieldLabel class
33389  * @cfg {String} html contents of the element
33390  * @cfg {String} tag tag of the element default label
33391  * @cfg {String} cls class of the element
33392  * @cfg {String} target label target 
33393  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33394  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33395  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33396  * @cfg {String} iconTooltip default "This field is required"
33397  * @cfg {String} indicatorpos (left|right) default left
33398  * 
33399  * @constructor
33400  * Create a new FieldLabel
33401  * @param {Object} config The config object
33402  */
33403
33404 Roo.bootstrap.form.FieldLabel = function(config){
33405     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33406     
33407     this.addEvents({
33408             /**
33409              * @event invalid
33410              * Fires after the field has been marked as invalid.
33411              * @param {Roo.form.FieldLabel} this
33412              * @param {String} msg The validation message
33413              */
33414             invalid : true,
33415             /**
33416              * @event valid
33417              * Fires after the field has been validated with no errors.
33418              * @param {Roo.form.FieldLabel} this
33419              */
33420             valid : true
33421         });
33422 };
33423
33424 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33425     
33426     tag: 'label',
33427     cls: '',
33428     html: '',
33429     target: '',
33430     allowBlank : true,
33431     invalidClass : 'has-warning',
33432     validClass : 'has-success',
33433     iconTooltip : 'This field is required',
33434     indicatorpos : 'left',
33435     
33436     getAutoCreate : function(){
33437         
33438         var cls = "";
33439         if (!this.allowBlank) {
33440             cls  = "visible";
33441         }
33442         
33443         var cfg = {
33444             tag : this.tag,
33445             cls : 'roo-bootstrap-field-label ' + this.cls,
33446             for : this.target,
33447             cn : [
33448                 {
33449                     tag : 'i',
33450                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33451                     tooltip : this.iconTooltip
33452                 },
33453                 {
33454                     tag : 'span',
33455                     html : this.html
33456                 }
33457             ] 
33458         };
33459         
33460         if(this.indicatorpos == 'right'){
33461             var cfg = {
33462                 tag : this.tag,
33463                 cls : 'roo-bootstrap-field-label ' + this.cls,
33464                 for : this.target,
33465                 cn : [
33466                     {
33467                         tag : 'span',
33468                         html : this.html
33469                     },
33470                     {
33471                         tag : 'i',
33472                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33473                         tooltip : this.iconTooltip
33474                     }
33475                 ] 
33476             };
33477         }
33478         
33479         return cfg;
33480     },
33481     
33482     initEvents: function() 
33483     {
33484         Roo.bootstrap.Element.superclass.initEvents.call(this);
33485         
33486         this.indicator = this.indicatorEl();
33487         
33488         if(this.indicator){
33489             this.indicator.removeClass('visible');
33490             this.indicator.addClass('invisible');
33491         }
33492         
33493         Roo.bootstrap.form.FieldLabel.register(this);
33494     },
33495     
33496     indicatorEl : function()
33497     {
33498         var indicator = this.el.select('i.roo-required-indicator',true).first();
33499         
33500         if(!indicator){
33501             return false;
33502         }
33503         
33504         return indicator;
33505         
33506     },
33507     
33508     /**
33509      * Mark this field as valid
33510      */
33511     markValid : function()
33512     {
33513         if(this.indicator){
33514             this.indicator.removeClass('visible');
33515             this.indicator.addClass('invisible');
33516         }
33517         if (Roo.bootstrap.version == 3) {
33518             this.el.removeClass(this.invalidClass);
33519             this.el.addClass(this.validClass);
33520         } else {
33521             this.el.removeClass('is-invalid');
33522             this.el.addClass('is-valid');
33523         }
33524         
33525         
33526         this.fireEvent('valid', this);
33527     },
33528     
33529     /**
33530      * Mark this field as invalid
33531      * @param {String} msg The validation message
33532      */
33533     markInvalid : function(msg)
33534     {
33535         if(this.indicator){
33536             this.indicator.removeClass('invisible');
33537             this.indicator.addClass('visible');
33538         }
33539           if (Roo.bootstrap.version == 3) {
33540             this.el.removeClass(this.validClass);
33541             this.el.addClass(this.invalidClass);
33542         } else {
33543             this.el.removeClass('is-valid');
33544             this.el.addClass('is-invalid');
33545         }
33546         
33547         
33548         this.fireEvent('invalid', this, msg);
33549     }
33550     
33551    
33552 });
33553
33554 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33555     
33556     groups: {},
33557     
33558      /**
33559     * register a FieldLabel Group
33560     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33561     */
33562     register : function(label)
33563     {
33564         if(this.groups.hasOwnProperty(label.target)){
33565             return;
33566         }
33567      
33568         this.groups[label.target] = label;
33569         
33570     },
33571     /**
33572     * fetch a FieldLabel Group based on the target
33573     * @param {string} target
33574     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33575     */
33576     get: function(target) {
33577         if (typeof(this.groups[target]) == 'undefined') {
33578             return false;
33579         }
33580         
33581         return this.groups[target] ;
33582     }
33583 });
33584
33585  
33586
33587  /*
33588  * - LGPL
33589  *
33590  * page DateSplitField.
33591  * 
33592  */
33593
33594
33595 /**
33596  * @class Roo.bootstrap.form.DateSplitField
33597  * @extends Roo.bootstrap.Component
33598  * Bootstrap DateSplitField class
33599  * @cfg {string} fieldLabel - the label associated
33600  * @cfg {Number} labelWidth set the width of label (0-12)
33601  * @cfg {String} labelAlign (top|left)
33602  * @cfg {Boolean} dayAllowBlank (true|false) default false
33603  * @cfg {Boolean} monthAllowBlank (true|false) default false
33604  * @cfg {Boolean} yearAllowBlank (true|false) default false
33605  * @cfg {string} dayPlaceholder 
33606  * @cfg {string} monthPlaceholder
33607  * @cfg {string} yearPlaceholder
33608  * @cfg {string} dayFormat default 'd'
33609  * @cfg {string} monthFormat default 'm'
33610  * @cfg {string} yearFormat default 'Y'
33611  * @cfg {Number} labellg set the width of label (1-12)
33612  * @cfg {Number} labelmd set the width of label (1-12)
33613  * @cfg {Number} labelsm set the width of label (1-12)
33614  * @cfg {Number} labelxs set the width of label (1-12)
33615
33616  *     
33617  * @constructor
33618  * Create a new DateSplitField
33619  * @param {Object} config The config object
33620  */
33621
33622 Roo.bootstrap.form.DateSplitField = function(config){
33623     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33624     
33625     this.addEvents({
33626         // raw events
33627          /**
33628          * @event years
33629          * getting the data of years
33630          * @param {Roo.bootstrap.form.DateSplitField} this
33631          * @param {Object} years
33632          */
33633         "years" : true,
33634         /**
33635          * @event days
33636          * getting the data of days
33637          * @param {Roo.bootstrap.form.DateSplitField} this
33638          * @param {Object} days
33639          */
33640         "days" : true,
33641         /**
33642          * @event invalid
33643          * Fires after the field has been marked as invalid.
33644          * @param {Roo.form.Field} this
33645          * @param {String} msg The validation message
33646          */
33647         invalid : true,
33648        /**
33649          * @event valid
33650          * Fires after the field has been validated with no errors.
33651          * @param {Roo.form.Field} this
33652          */
33653         valid : true
33654     });
33655 };
33656
33657 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33658     
33659     fieldLabel : '',
33660     labelAlign : 'top',
33661     labelWidth : 3,
33662     dayAllowBlank : false,
33663     monthAllowBlank : false,
33664     yearAllowBlank : false,
33665     dayPlaceholder : '',
33666     monthPlaceholder : '',
33667     yearPlaceholder : '',
33668     dayFormat : 'd',
33669     monthFormat : 'm',
33670     yearFormat : 'Y',
33671     isFormField : true,
33672     labellg : 0,
33673     labelmd : 0,
33674     labelsm : 0,
33675     labelxs : 0,
33676     
33677     getAutoCreate : function()
33678     {
33679         var cfg = {
33680             tag : 'div',
33681             cls : 'row roo-date-split-field-group',
33682             cn : [
33683                 {
33684                     tag : 'input',
33685                     type : 'hidden',
33686                     cls : 'form-hidden-field roo-date-split-field-group-value',
33687                     name : this.name
33688                 }
33689             ]
33690         };
33691         
33692         var labelCls = 'col-md-12';
33693         var contentCls = 'col-md-4';
33694         
33695         if(this.fieldLabel){
33696             
33697             var label = {
33698                 tag : 'div',
33699                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33700                 cn : [
33701                     {
33702                         tag : 'label',
33703                         html : this.fieldLabel
33704                     }
33705                 ]
33706             };
33707             
33708             if(this.labelAlign == 'left'){
33709             
33710                 if(this.labelWidth > 12){
33711                     label.style = "width: " + this.labelWidth + 'px';
33712                 }
33713
33714                 if(this.labelWidth < 13 && this.labelmd == 0){
33715                     this.labelmd = this.labelWidth;
33716                 }
33717
33718                 if(this.labellg > 0){
33719                     labelCls = ' col-lg-' + this.labellg;
33720                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33721                 }
33722
33723                 if(this.labelmd > 0){
33724                     labelCls = ' col-md-' + this.labelmd;
33725                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33726                 }
33727
33728                 if(this.labelsm > 0){
33729                     labelCls = ' col-sm-' + this.labelsm;
33730                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33731                 }
33732
33733                 if(this.labelxs > 0){
33734                     labelCls = ' col-xs-' + this.labelxs;
33735                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33736                 }
33737             }
33738             
33739             label.cls += ' ' + labelCls;
33740             
33741             cfg.cn.push(label);
33742         }
33743         
33744         Roo.each(['day', 'month', 'year'], function(t){
33745             cfg.cn.push({
33746                 tag : 'div',
33747                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33748             });
33749         }, this);
33750         
33751         return cfg;
33752     },
33753     
33754     inputEl: function ()
33755     {
33756         return this.el.select('.roo-date-split-field-group-value', true).first();
33757     },
33758     
33759     onRender : function(ct, position) 
33760     {
33761         var _this = this;
33762         
33763         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33764         
33765         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33766         
33767         this.dayField = new Roo.bootstrap.form.ComboBox({
33768             allowBlank : this.dayAllowBlank,
33769             alwaysQuery : true,
33770             displayField : 'value',
33771             editable : false,
33772             fieldLabel : '',
33773             forceSelection : true,
33774             mode : 'local',
33775             placeholder : this.dayPlaceholder,
33776             selectOnFocus : true,
33777             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33778             triggerAction : 'all',
33779             typeAhead : true,
33780             valueField : 'value',
33781             store : new Roo.data.SimpleStore({
33782                 data : (function() {    
33783                     var days = [];
33784                     _this.fireEvent('days', _this, days);
33785                     return days;
33786                 })(),
33787                 fields : [ 'value' ]
33788             }),
33789             listeners : {
33790                 select : function (_self, record, index)
33791                 {
33792                     _this.setValue(_this.getValue());
33793                 }
33794             }
33795         });
33796
33797         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33798         
33799         this.monthField = new Roo.bootstrap.form.MonthField({
33800             after : '<i class=\"fa fa-calendar\"></i>',
33801             allowBlank : this.monthAllowBlank,
33802             placeholder : this.monthPlaceholder,
33803             readOnly : true,
33804             listeners : {
33805                 render : function (_self)
33806                 {
33807                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33808                         e.preventDefault();
33809                         _self.focus();
33810                     });
33811                 },
33812                 select : function (_self, oldvalue, newvalue)
33813                 {
33814                     _this.setValue(_this.getValue());
33815                 }
33816             }
33817         });
33818         
33819         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33820         
33821         this.yearField = new Roo.bootstrap.form.ComboBox({
33822             allowBlank : this.yearAllowBlank,
33823             alwaysQuery : true,
33824             displayField : 'value',
33825             editable : false,
33826             fieldLabel : '',
33827             forceSelection : true,
33828             mode : 'local',
33829             placeholder : this.yearPlaceholder,
33830             selectOnFocus : true,
33831             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33832             triggerAction : 'all',
33833             typeAhead : true,
33834             valueField : 'value',
33835             store : new Roo.data.SimpleStore({
33836                 data : (function() {
33837                     var years = [];
33838                     _this.fireEvent('years', _this, years);
33839                     return years;
33840                 })(),
33841                 fields : [ 'value' ]
33842             }),
33843             listeners : {
33844                 select : function (_self, record, index)
33845                 {
33846                     _this.setValue(_this.getValue());
33847                 }
33848             }
33849         });
33850
33851         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33852     },
33853     
33854     setValue : function(v, format)
33855     {
33856         this.inputEl.dom.value = v;
33857         
33858         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33859         
33860         var d = Date.parseDate(v, f);
33861         
33862         if(!d){
33863             this.validate();
33864             return;
33865         }
33866         
33867         this.setDay(d.format(this.dayFormat));
33868         this.setMonth(d.format(this.monthFormat));
33869         this.setYear(d.format(this.yearFormat));
33870         
33871         this.validate();
33872         
33873         return;
33874     },
33875     
33876     setDay : function(v)
33877     {
33878         this.dayField.setValue(v);
33879         this.inputEl.dom.value = this.getValue();
33880         this.validate();
33881         return;
33882     },
33883     
33884     setMonth : function(v)
33885     {
33886         this.monthField.setValue(v, true);
33887         this.inputEl.dom.value = this.getValue();
33888         this.validate();
33889         return;
33890     },
33891     
33892     setYear : function(v)
33893     {
33894         this.yearField.setValue(v);
33895         this.inputEl.dom.value = this.getValue();
33896         this.validate();
33897         return;
33898     },
33899     
33900     getDay : function()
33901     {
33902         return this.dayField.getValue();
33903     },
33904     
33905     getMonth : function()
33906     {
33907         return this.monthField.getValue();
33908     },
33909     
33910     getYear : function()
33911     {
33912         return this.yearField.getValue();
33913     },
33914     
33915     getValue : function()
33916     {
33917         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33918         
33919         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33920         
33921         return date;
33922     },
33923     
33924     reset : function()
33925     {
33926         this.setDay('');
33927         this.setMonth('');
33928         this.setYear('');
33929         this.inputEl.dom.value = '';
33930         this.validate();
33931         return;
33932     },
33933     
33934     validate : function()
33935     {
33936         var d = this.dayField.validate();
33937         var m = this.monthField.validate();
33938         var y = this.yearField.validate();
33939         
33940         var valid = true;
33941         
33942         if(
33943                 (!this.dayAllowBlank && !d) ||
33944                 (!this.monthAllowBlank && !m) ||
33945                 (!this.yearAllowBlank && !y)
33946         ){
33947             valid = false;
33948         }
33949         
33950         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33951             return valid;
33952         }
33953         
33954         if(valid){
33955             this.markValid();
33956             return valid;
33957         }
33958         
33959         this.markInvalid();
33960         
33961         return valid;
33962     },
33963     
33964     markValid : function()
33965     {
33966         
33967         var label = this.el.select('label', true).first();
33968         var icon = this.el.select('i.fa-star', true).first();
33969
33970         if(label && icon){
33971             icon.remove();
33972         }
33973         
33974         this.fireEvent('valid', this);
33975     },
33976     
33977      /**
33978      * Mark this field as invalid
33979      * @param {String} msg The validation message
33980      */
33981     markInvalid : function(msg)
33982     {
33983         
33984         var label = this.el.select('label', true).first();
33985         var icon = this.el.select('i.fa-star', true).first();
33986
33987         if(label && !icon){
33988             this.el.select('.roo-date-split-field-label', true).createChild({
33989                 tag : 'i',
33990                 cls : 'text-danger fa fa-lg fa-star',
33991                 tooltip : 'This field is required',
33992                 style : 'margin-right:5px;'
33993             }, label, true);
33994         }
33995         
33996         this.fireEvent('invalid', this, msg);
33997     },
33998     
33999     clearInvalid : function()
34000     {
34001         var label = this.el.select('label', true).first();
34002         var icon = this.el.select('i.fa-star', true).first();
34003
34004         if(label && icon){
34005             icon.remove();
34006         }
34007         
34008         this.fireEvent('valid', this);
34009     },
34010     
34011     getName: function()
34012     {
34013         return this.name;
34014     }
34015     
34016 });
34017
34018  
34019
34020 /**
34021  * @class Roo.bootstrap.LayoutMasonry
34022  * @extends Roo.bootstrap.Component
34023  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34024  * Bootstrap Layout Masonry class
34025  *
34026  * This is based on 
34027  * http://masonry.desandro.com
34028  *
34029  * The idea is to render all the bricks based on vertical width...
34030  *
34031  * The original code extends 'outlayer' - we might need to use that....
34032
34033  * @constructor
34034  * Create a new Element
34035  * @param {Object} config The config object
34036  */
34037
34038 Roo.bootstrap.LayoutMasonry = function(config){
34039     
34040     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34041     
34042     this.bricks = [];
34043     
34044     Roo.bootstrap.LayoutMasonry.register(this);
34045     
34046     this.addEvents({
34047         // raw events
34048         /**
34049          * @event layout
34050          * Fire after layout the items
34051          * @param {Roo.bootstrap.LayoutMasonry} this
34052          * @param {Roo.EventObject} e
34053          */
34054         "layout" : true
34055     });
34056     
34057 };
34058
34059 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34060     
34061     /**
34062      * @cfg {Boolean} isLayoutInstant = no animation?
34063      */   
34064     isLayoutInstant : false, // needed?
34065    
34066     /**
34067      * @cfg {Number} boxWidth  width of the columns
34068      */   
34069     boxWidth : 450,
34070     
34071       /**
34072      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34073      */   
34074     boxHeight : 0,
34075     
34076     /**
34077      * @cfg {Number} padWidth padding below box..
34078      */   
34079     padWidth : 10, 
34080     
34081     /**
34082      * @cfg {Number} gutter gutter width..
34083      */   
34084     gutter : 10,
34085     
34086      /**
34087      * @cfg {Number} maxCols maximum number of columns
34088      */   
34089     
34090     maxCols: 0,
34091     
34092     /**
34093      * @cfg {Boolean} isAutoInitial defalut true
34094      */   
34095     isAutoInitial : true, 
34096     
34097     containerWidth: 0,
34098     
34099     /**
34100      * @cfg {Boolean} isHorizontal defalut false
34101      */   
34102     isHorizontal : false, 
34103
34104     currentSize : null,
34105     
34106     tag: 'div',
34107     
34108     cls: '',
34109     
34110     bricks: null, //CompositeElement
34111     
34112     cols : 1,
34113     
34114     _isLayoutInited : false,
34115     
34116 //    isAlternative : false, // only use for vertical layout...
34117     
34118     /**
34119      * @cfg {Number} alternativePadWidth padding below box..
34120      */   
34121     alternativePadWidth : 50,
34122     
34123     selectedBrick : [],
34124     
34125     getAutoCreate : function(){
34126         
34127         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34128         
34129         var cfg = {
34130             tag: this.tag,
34131             cls: 'blog-masonary-wrapper ' + this.cls,
34132             cn : {
34133                 cls : 'mas-boxes masonary'
34134             }
34135         };
34136         
34137         return cfg;
34138     },
34139     
34140     getChildContainer: function( )
34141     {
34142         if (this.boxesEl) {
34143             return this.boxesEl;
34144         }
34145         
34146         this.boxesEl = this.el.select('.mas-boxes').first();
34147         
34148         return this.boxesEl;
34149     },
34150     
34151     
34152     initEvents : function()
34153     {
34154         var _this = this;
34155         
34156         if(this.isAutoInitial){
34157             Roo.log('hook children rendered');
34158             this.on('childrenrendered', function() {
34159                 Roo.log('children rendered');
34160                 _this.initial();
34161             } ,this);
34162         }
34163     },
34164     
34165     initial : function()
34166     {
34167         this.selectedBrick = [];
34168         
34169         this.currentSize = this.el.getBox(true);
34170         
34171         Roo.EventManager.onWindowResize(this.resize, this); 
34172
34173         if(!this.isAutoInitial){
34174             this.layout();
34175             return;
34176         }
34177         
34178         this.layout();
34179         
34180         return;
34181         //this.layout.defer(500,this);
34182         
34183     },
34184     
34185     resize : function()
34186     {
34187         var cs = this.el.getBox(true);
34188         
34189         if (
34190                 this.currentSize.width == cs.width && 
34191                 this.currentSize.x == cs.x && 
34192                 this.currentSize.height == cs.height && 
34193                 this.currentSize.y == cs.y 
34194         ) {
34195             Roo.log("no change in with or X or Y");
34196             return;
34197         }
34198         
34199         this.currentSize = cs;
34200         
34201         this.layout();
34202         
34203     },
34204     
34205     layout : function()
34206     {   
34207         this._resetLayout();
34208         
34209         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34210         
34211         this.layoutItems( isInstant );
34212       
34213         this._isLayoutInited = true;
34214         
34215         this.fireEvent('layout', this);
34216         
34217     },
34218     
34219     _resetLayout : function()
34220     {
34221         if(this.isHorizontal){
34222             this.horizontalMeasureColumns();
34223             return;
34224         }
34225         
34226         this.verticalMeasureColumns();
34227         
34228     },
34229     
34230     verticalMeasureColumns : function()
34231     {
34232         this.getContainerWidth();
34233         
34234 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34235 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34236 //            return;
34237 //        }
34238         
34239         var boxWidth = this.boxWidth + this.padWidth;
34240         
34241         if(this.containerWidth < this.boxWidth){
34242             boxWidth = this.containerWidth
34243         }
34244         
34245         var containerWidth = this.containerWidth;
34246         
34247         var cols = Math.floor(containerWidth / boxWidth);
34248         
34249         this.cols = Math.max( cols, 1 );
34250         
34251         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34252         
34253         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34254         
34255         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34256         
34257         this.colWidth = boxWidth + avail - this.padWidth;
34258         
34259         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34260         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34261     },
34262     
34263     horizontalMeasureColumns : function()
34264     {
34265         this.getContainerWidth();
34266         
34267         var boxWidth = this.boxWidth;
34268         
34269         if(this.containerWidth < boxWidth){
34270             boxWidth = this.containerWidth;
34271         }
34272         
34273         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34274         
34275         this.el.setHeight(boxWidth);
34276         
34277     },
34278     
34279     getContainerWidth : function()
34280     {
34281         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34282     },
34283     
34284     layoutItems : function( isInstant )
34285     {
34286         Roo.log(this.bricks);
34287         
34288         var items = Roo.apply([], this.bricks);
34289         
34290         if(this.isHorizontal){
34291             this._horizontalLayoutItems( items , isInstant );
34292             return;
34293         }
34294         
34295 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34296 //            this._verticalAlternativeLayoutItems( items , isInstant );
34297 //            return;
34298 //        }
34299         
34300         this._verticalLayoutItems( items , isInstant );
34301         
34302     },
34303     
34304     _verticalLayoutItems : function ( items , isInstant)
34305     {
34306         if ( !items || !items.length ) {
34307             return;
34308         }
34309         
34310         var standard = [
34311             ['xs', 'xs', 'xs', 'tall'],
34312             ['xs', 'xs', 'tall'],
34313             ['xs', 'xs', 'sm'],
34314             ['xs', 'xs', 'xs'],
34315             ['xs', 'tall'],
34316             ['xs', 'sm'],
34317             ['xs', 'xs'],
34318             ['xs'],
34319             
34320             ['sm', 'xs', 'xs'],
34321             ['sm', 'xs'],
34322             ['sm'],
34323             
34324             ['tall', 'xs', 'xs', 'xs'],
34325             ['tall', 'xs', 'xs'],
34326             ['tall', 'xs'],
34327             ['tall']
34328             
34329         ];
34330         
34331         var queue = [];
34332         
34333         var boxes = [];
34334         
34335         var box = [];
34336         
34337         Roo.each(items, function(item, k){
34338             
34339             switch (item.size) {
34340                 // these layouts take up a full box,
34341                 case 'md' :
34342                 case 'md-left' :
34343                 case 'md-right' :
34344                 case 'wide' :
34345                     
34346                     if(box.length){
34347                         boxes.push(box);
34348                         box = [];
34349                     }
34350                     
34351                     boxes.push([item]);
34352                     
34353                     break;
34354                     
34355                 case 'xs' :
34356                 case 'sm' :
34357                 case 'tall' :
34358                     
34359                     box.push(item);
34360                     
34361                     break;
34362                 default :
34363                     break;
34364                     
34365             }
34366             
34367         }, this);
34368         
34369         if(box.length){
34370             boxes.push(box);
34371             box = [];
34372         }
34373         
34374         var filterPattern = function(box, length)
34375         {
34376             if(!box.length){
34377                 return;
34378             }
34379             
34380             var match = false;
34381             
34382             var pattern = box.slice(0, length);
34383             
34384             var format = [];
34385             
34386             Roo.each(pattern, function(i){
34387                 format.push(i.size);
34388             }, this);
34389             
34390             Roo.each(standard, function(s){
34391                 
34392                 if(String(s) != String(format)){
34393                     return;
34394                 }
34395                 
34396                 match = true;
34397                 return false;
34398                 
34399             }, this);
34400             
34401             if(!match && length == 1){
34402                 return;
34403             }
34404             
34405             if(!match){
34406                 filterPattern(box, length - 1);
34407                 return;
34408             }
34409                 
34410             queue.push(pattern);
34411
34412             box = box.slice(length, box.length);
34413
34414             filterPattern(box, 4);
34415
34416             return;
34417             
34418         }
34419         
34420         Roo.each(boxes, function(box, k){
34421             
34422             if(!box.length){
34423                 return;
34424             }
34425             
34426             if(box.length == 1){
34427                 queue.push(box);
34428                 return;
34429             }
34430             
34431             filterPattern(box, 4);
34432             
34433         }, this);
34434         
34435         this._processVerticalLayoutQueue( queue, isInstant );
34436         
34437     },
34438     
34439 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34440 //    {
34441 //        if ( !items || !items.length ) {
34442 //            return;
34443 //        }
34444 //
34445 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34446 //        
34447 //    },
34448     
34449     _horizontalLayoutItems : function ( items , isInstant)
34450     {
34451         if ( !items || !items.length || items.length < 3) {
34452             return;
34453         }
34454         
34455         items.reverse();
34456         
34457         var eItems = items.slice(0, 3);
34458         
34459         items = items.slice(3, items.length);
34460         
34461         var standard = [
34462             ['xs', 'xs', 'xs', 'wide'],
34463             ['xs', 'xs', 'wide'],
34464             ['xs', 'xs', 'sm'],
34465             ['xs', 'xs', 'xs'],
34466             ['xs', 'wide'],
34467             ['xs', 'sm'],
34468             ['xs', 'xs'],
34469             ['xs'],
34470             
34471             ['sm', 'xs', 'xs'],
34472             ['sm', 'xs'],
34473             ['sm'],
34474             
34475             ['wide', 'xs', 'xs', 'xs'],
34476             ['wide', 'xs', 'xs'],
34477             ['wide', 'xs'],
34478             ['wide'],
34479             
34480             ['wide-thin']
34481         ];
34482         
34483         var queue = [];
34484         
34485         var boxes = [];
34486         
34487         var box = [];
34488         
34489         Roo.each(items, function(item, k){
34490             
34491             switch (item.size) {
34492                 case 'md' :
34493                 case 'md-left' :
34494                 case 'md-right' :
34495                 case 'tall' :
34496                     
34497                     if(box.length){
34498                         boxes.push(box);
34499                         box = [];
34500                     }
34501                     
34502                     boxes.push([item]);
34503                     
34504                     break;
34505                     
34506                 case 'xs' :
34507                 case 'sm' :
34508                 case 'wide' :
34509                 case 'wide-thin' :
34510                     
34511                     box.push(item);
34512                     
34513                     break;
34514                 default :
34515                     break;
34516                     
34517             }
34518             
34519         }, this);
34520         
34521         if(box.length){
34522             boxes.push(box);
34523             box = [];
34524         }
34525         
34526         var filterPattern = function(box, length)
34527         {
34528             if(!box.length){
34529                 return;
34530             }
34531             
34532             var match = false;
34533             
34534             var pattern = box.slice(0, length);
34535             
34536             var format = [];
34537             
34538             Roo.each(pattern, function(i){
34539                 format.push(i.size);
34540             }, this);
34541             
34542             Roo.each(standard, function(s){
34543                 
34544                 if(String(s) != String(format)){
34545                     return;
34546                 }
34547                 
34548                 match = true;
34549                 return false;
34550                 
34551             }, this);
34552             
34553             if(!match && length == 1){
34554                 return;
34555             }
34556             
34557             if(!match){
34558                 filterPattern(box, length - 1);
34559                 return;
34560             }
34561                 
34562             queue.push(pattern);
34563
34564             box = box.slice(length, box.length);
34565
34566             filterPattern(box, 4);
34567
34568             return;
34569             
34570         }
34571         
34572         Roo.each(boxes, function(box, k){
34573             
34574             if(!box.length){
34575                 return;
34576             }
34577             
34578             if(box.length == 1){
34579                 queue.push(box);
34580                 return;
34581             }
34582             
34583             filterPattern(box, 4);
34584             
34585         }, this);
34586         
34587         
34588         var prune = [];
34589         
34590         var pos = this.el.getBox(true);
34591         
34592         var minX = pos.x;
34593         
34594         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34595         
34596         var hit_end = false;
34597         
34598         Roo.each(queue, function(box){
34599             
34600             if(hit_end){
34601                 
34602                 Roo.each(box, function(b){
34603                 
34604                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34605                     b.el.hide();
34606
34607                 }, this);
34608
34609                 return;
34610             }
34611             
34612             var mx = 0;
34613             
34614             Roo.each(box, function(b){
34615                 
34616                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34617                 b.el.show();
34618
34619                 mx = Math.max(mx, b.x);
34620                 
34621             }, this);
34622             
34623             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34624             
34625             if(maxX < minX){
34626                 
34627                 Roo.each(box, function(b){
34628                 
34629                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34630                     b.el.hide();
34631                     
34632                 }, this);
34633                 
34634                 hit_end = true;
34635                 
34636                 return;
34637             }
34638             
34639             prune.push(box);
34640             
34641         }, this);
34642         
34643         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34644     },
34645     
34646     /** Sets position of item in DOM
34647     * @param {Element} item
34648     * @param {Number} x - horizontal position
34649     * @param {Number} y - vertical position
34650     * @param {Boolean} isInstant - disables transitions
34651     */
34652     _processVerticalLayoutQueue : function( queue, isInstant )
34653     {
34654         var pos = this.el.getBox(true);
34655         var x = pos.x;
34656         var y = pos.y;
34657         var maxY = [];
34658         
34659         for (var i = 0; i < this.cols; i++){
34660             maxY[i] = pos.y;
34661         }
34662         
34663         Roo.each(queue, function(box, k){
34664             
34665             var col = k % this.cols;
34666             
34667             Roo.each(box, function(b,kk){
34668                 
34669                 b.el.position('absolute');
34670                 
34671                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34672                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34673                 
34674                 if(b.size == 'md-left' || b.size == 'md-right'){
34675                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34676                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34677                 }
34678                 
34679                 b.el.setWidth(width);
34680                 b.el.setHeight(height);
34681                 // iframe?
34682                 b.el.select('iframe',true).setSize(width,height);
34683                 
34684             }, this);
34685             
34686             for (var i = 0; i < this.cols; i++){
34687                 
34688                 if(maxY[i] < maxY[col]){
34689                     col = i;
34690                     continue;
34691                 }
34692                 
34693                 col = Math.min(col, i);
34694                 
34695             }
34696             
34697             x = pos.x + col * (this.colWidth + this.padWidth);
34698             
34699             y = maxY[col];
34700             
34701             var positions = [];
34702             
34703             switch (box.length){
34704                 case 1 :
34705                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34706                     break;
34707                 case 2 :
34708                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34709                     break;
34710                 case 3 :
34711                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34712                     break;
34713                 case 4 :
34714                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34715                     break;
34716                 default :
34717                     break;
34718             }
34719             
34720             Roo.each(box, function(b,kk){
34721                 
34722                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34723                 
34724                 var sz = b.el.getSize();
34725                 
34726                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34727                 
34728             }, this);
34729             
34730         }, this);
34731         
34732         var mY = 0;
34733         
34734         for (var i = 0; i < this.cols; i++){
34735             mY = Math.max(mY, maxY[i]);
34736         }
34737         
34738         this.el.setHeight(mY - pos.y);
34739         
34740     },
34741     
34742 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34743 //    {
34744 //        var pos = this.el.getBox(true);
34745 //        var x = pos.x;
34746 //        var y = pos.y;
34747 //        var maxX = pos.right;
34748 //        
34749 //        var maxHeight = 0;
34750 //        
34751 //        Roo.each(items, function(item, k){
34752 //            
34753 //            var c = k % 2;
34754 //            
34755 //            item.el.position('absolute');
34756 //                
34757 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34758 //
34759 //            item.el.setWidth(width);
34760 //
34761 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34762 //
34763 //            item.el.setHeight(height);
34764 //            
34765 //            if(c == 0){
34766 //                item.el.setXY([x, y], isInstant ? false : true);
34767 //            } else {
34768 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34769 //            }
34770 //            
34771 //            y = y + height + this.alternativePadWidth;
34772 //            
34773 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34774 //            
34775 //        }, this);
34776 //        
34777 //        this.el.setHeight(maxHeight);
34778 //        
34779 //    },
34780     
34781     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34782     {
34783         var pos = this.el.getBox(true);
34784         
34785         var minX = pos.x;
34786         var minY = pos.y;
34787         
34788         var maxX = pos.right;
34789         
34790         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34791         
34792         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34793         
34794         Roo.each(queue, function(box, k){
34795             
34796             Roo.each(box, function(b, kk){
34797                 
34798                 b.el.position('absolute');
34799                 
34800                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34801                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34802                 
34803                 if(b.size == 'md-left' || b.size == 'md-right'){
34804                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34805                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34806                 }
34807                 
34808                 b.el.setWidth(width);
34809                 b.el.setHeight(height);
34810                 
34811             }, this);
34812             
34813             if(!box.length){
34814                 return;
34815             }
34816             
34817             var positions = [];
34818             
34819             switch (box.length){
34820                 case 1 :
34821                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34822                     break;
34823                 case 2 :
34824                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34825                     break;
34826                 case 3 :
34827                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34828                     break;
34829                 case 4 :
34830                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34831                     break;
34832                 default :
34833                     break;
34834             }
34835             
34836             Roo.each(box, function(b,kk){
34837                 
34838                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34839                 
34840                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34841                 
34842             }, this);
34843             
34844         }, this);
34845         
34846     },
34847     
34848     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34849     {
34850         Roo.each(eItems, function(b,k){
34851             
34852             b.size = (k == 0) ? 'sm' : 'xs';
34853             b.x = (k == 0) ? 2 : 1;
34854             b.y = (k == 0) ? 2 : 1;
34855             
34856             b.el.position('absolute');
34857             
34858             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34859                 
34860             b.el.setWidth(width);
34861             
34862             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34863             
34864             b.el.setHeight(height);
34865             
34866         }, this);
34867
34868         var positions = [];
34869         
34870         positions.push({
34871             x : maxX - this.unitWidth * 2 - this.gutter,
34872             y : minY
34873         });
34874         
34875         positions.push({
34876             x : maxX - this.unitWidth,
34877             y : minY + (this.unitWidth + this.gutter) * 2
34878         });
34879         
34880         positions.push({
34881             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34882             y : minY
34883         });
34884         
34885         Roo.each(eItems, function(b,k){
34886             
34887             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34888
34889         }, this);
34890         
34891     },
34892     
34893     getVerticalOneBoxColPositions : function(x, y, box)
34894     {
34895         var pos = [];
34896         
34897         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34898         
34899         if(box[0].size == 'md-left'){
34900             rand = 0;
34901         }
34902         
34903         if(box[0].size == 'md-right'){
34904             rand = 1;
34905         }
34906         
34907         pos.push({
34908             x : x + (this.unitWidth + this.gutter) * rand,
34909             y : y
34910         });
34911         
34912         return pos;
34913     },
34914     
34915     getVerticalTwoBoxColPositions : function(x, y, box)
34916     {
34917         var pos = [];
34918         
34919         if(box[0].size == 'xs'){
34920             
34921             pos.push({
34922                 x : x,
34923                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34924             });
34925
34926             pos.push({
34927                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34928                 y : y
34929             });
34930             
34931             return pos;
34932             
34933         }
34934         
34935         pos.push({
34936             x : x,
34937             y : y
34938         });
34939
34940         pos.push({
34941             x : x + (this.unitWidth + this.gutter) * 2,
34942             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34943         });
34944         
34945         return pos;
34946         
34947     },
34948     
34949     getVerticalThreeBoxColPositions : function(x, y, box)
34950     {
34951         var pos = [];
34952         
34953         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34954             
34955             pos.push({
34956                 x : x,
34957                 y : y
34958             });
34959
34960             pos.push({
34961                 x : x + (this.unitWidth + this.gutter) * 1,
34962                 y : y
34963             });
34964             
34965             pos.push({
34966                 x : x + (this.unitWidth + this.gutter) * 2,
34967                 y : y
34968             });
34969             
34970             return pos;
34971             
34972         }
34973         
34974         if(box[0].size == 'xs' && box[1].size == 'xs'){
34975             
34976             pos.push({
34977                 x : x,
34978                 y : y
34979             });
34980
34981             pos.push({
34982                 x : x,
34983                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34984             });
34985             
34986             pos.push({
34987                 x : x + (this.unitWidth + this.gutter) * 1,
34988                 y : y
34989             });
34990             
34991             return pos;
34992             
34993         }
34994         
34995         pos.push({
34996             x : x,
34997             y : y
34998         });
34999
35000         pos.push({
35001             x : x + (this.unitWidth + this.gutter) * 2,
35002             y : y
35003         });
35004
35005         pos.push({
35006             x : x + (this.unitWidth + this.gutter) * 2,
35007             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35008         });
35009             
35010         return pos;
35011         
35012     },
35013     
35014     getVerticalFourBoxColPositions : function(x, y, box)
35015     {
35016         var pos = [];
35017         
35018         if(box[0].size == 'xs'){
35019             
35020             pos.push({
35021                 x : x,
35022                 y : y
35023             });
35024
35025             pos.push({
35026                 x : x,
35027                 y : y + (this.unitHeight + this.gutter) * 1
35028             });
35029             
35030             pos.push({
35031                 x : x,
35032                 y : y + (this.unitHeight + this.gutter) * 2
35033             });
35034             
35035             pos.push({
35036                 x : x + (this.unitWidth + this.gutter) * 1,
35037                 y : y
35038             });
35039             
35040             return pos;
35041             
35042         }
35043         
35044         pos.push({
35045             x : x,
35046             y : y
35047         });
35048
35049         pos.push({
35050             x : x + (this.unitWidth + this.gutter) * 2,
35051             y : y
35052         });
35053
35054         pos.push({
35055             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35056             y : y + (this.unitHeight + this.gutter) * 1
35057         });
35058
35059         pos.push({
35060             x : x + (this.unitWidth + this.gutter) * 2,
35061             y : y + (this.unitWidth + this.gutter) * 2
35062         });
35063
35064         return pos;
35065         
35066     },
35067     
35068     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35069     {
35070         var pos = [];
35071         
35072         if(box[0].size == 'md-left'){
35073             pos.push({
35074                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35075                 y : minY
35076             });
35077             
35078             return pos;
35079         }
35080         
35081         if(box[0].size == 'md-right'){
35082             pos.push({
35083                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35084                 y : minY + (this.unitWidth + this.gutter) * 1
35085             });
35086             
35087             return pos;
35088         }
35089         
35090         var rand = Math.floor(Math.random() * (4 - box[0].y));
35091         
35092         pos.push({
35093             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35094             y : minY + (this.unitWidth + this.gutter) * rand
35095         });
35096         
35097         return pos;
35098         
35099     },
35100     
35101     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35102     {
35103         var pos = [];
35104         
35105         if(box[0].size == 'xs'){
35106             
35107             pos.push({
35108                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35109                 y : minY
35110             });
35111
35112             pos.push({
35113                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35114                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35115             });
35116             
35117             return pos;
35118             
35119         }
35120         
35121         pos.push({
35122             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35123             y : minY
35124         });
35125
35126         pos.push({
35127             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35128             y : minY + (this.unitWidth + this.gutter) * 2
35129         });
35130         
35131         return pos;
35132         
35133     },
35134     
35135     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35136     {
35137         var pos = [];
35138         
35139         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35140             
35141             pos.push({
35142                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35143                 y : minY
35144             });
35145
35146             pos.push({
35147                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35148                 y : minY + (this.unitWidth + this.gutter) * 1
35149             });
35150             
35151             pos.push({
35152                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35153                 y : minY + (this.unitWidth + this.gutter) * 2
35154             });
35155             
35156             return pos;
35157             
35158         }
35159         
35160         if(box[0].size == 'xs' && box[1].size == 'xs'){
35161             
35162             pos.push({
35163                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35164                 y : minY
35165             });
35166
35167             pos.push({
35168                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35169                 y : minY
35170             });
35171             
35172             pos.push({
35173                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35174                 y : minY + (this.unitWidth + this.gutter) * 1
35175             });
35176             
35177             return pos;
35178             
35179         }
35180         
35181         pos.push({
35182             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35183             y : minY
35184         });
35185
35186         pos.push({
35187             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35188             y : minY + (this.unitWidth + this.gutter) * 2
35189         });
35190
35191         pos.push({
35192             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35193             y : minY + (this.unitWidth + this.gutter) * 2
35194         });
35195             
35196         return pos;
35197         
35198     },
35199     
35200     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35201     {
35202         var pos = [];
35203         
35204         if(box[0].size == 'xs'){
35205             
35206             pos.push({
35207                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35208                 y : minY
35209             });
35210
35211             pos.push({
35212                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35213                 y : minY
35214             });
35215             
35216             pos.push({
35217                 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),
35218                 y : minY
35219             });
35220             
35221             pos.push({
35222                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35223                 y : minY + (this.unitWidth + this.gutter) * 1
35224             });
35225             
35226             return pos;
35227             
35228         }
35229         
35230         pos.push({
35231             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35232             y : minY
35233         });
35234         
35235         pos.push({
35236             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35237             y : minY + (this.unitWidth + this.gutter) * 2
35238         });
35239         
35240         pos.push({
35241             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35242             y : minY + (this.unitWidth + this.gutter) * 2
35243         });
35244         
35245         pos.push({
35246             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),
35247             y : minY + (this.unitWidth + this.gutter) * 2
35248         });
35249
35250         return pos;
35251         
35252     },
35253     
35254     /**
35255     * remove a Masonry Brick
35256     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35257     */
35258     removeBrick : function(brick_id)
35259     {
35260         if (!brick_id) {
35261             return;
35262         }
35263         
35264         for (var i = 0; i<this.bricks.length; i++) {
35265             if (this.bricks[i].id == brick_id) {
35266                 this.bricks.splice(i,1);
35267                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35268                 this.initial();
35269             }
35270         }
35271     },
35272     
35273     /**
35274     * adds a Masonry Brick
35275     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35276     */
35277     addBrick : function(cfg)
35278     {
35279         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35280         //this.register(cn);
35281         cn.parentId = this.id;
35282         cn.render(this.el);
35283         return cn;
35284     },
35285     
35286     /**
35287     * register a Masonry Brick
35288     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35289     */
35290     
35291     register : function(brick)
35292     {
35293         this.bricks.push(brick);
35294         brick.masonryId = this.id;
35295     },
35296     
35297     /**
35298     * clear all the Masonry Brick
35299     */
35300     clearAll : function()
35301     {
35302         this.bricks = [];
35303         //this.getChildContainer().dom.innerHTML = "";
35304         this.el.dom.innerHTML = '';
35305     },
35306     
35307     getSelected : function()
35308     {
35309         if (!this.selectedBrick) {
35310             return false;
35311         }
35312         
35313         return this.selectedBrick;
35314     }
35315 });
35316
35317 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35318     
35319     groups: {},
35320      /**
35321     * register a Masonry Layout
35322     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35323     */
35324     
35325     register : function(layout)
35326     {
35327         this.groups[layout.id] = layout;
35328     },
35329     /**
35330     * fetch a  Masonry Layout based on the masonry layout ID
35331     * @param {string} the masonry layout to add
35332     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35333     */
35334     
35335     get: function(layout_id) {
35336         if (typeof(this.groups[layout_id]) == 'undefined') {
35337             return false;
35338         }
35339         return this.groups[layout_id] ;
35340     }
35341     
35342     
35343     
35344 });
35345
35346  
35347
35348  /**
35349  *
35350  * This is based on 
35351  * http://masonry.desandro.com
35352  *
35353  * The idea is to render all the bricks based on vertical width...
35354  *
35355  * The original code extends 'outlayer' - we might need to use that....
35356  * 
35357  */
35358
35359
35360 /**
35361  * @class Roo.bootstrap.LayoutMasonryAuto
35362  * @extends Roo.bootstrap.Component
35363  * Bootstrap Layout Masonry class
35364  * 
35365  * @constructor
35366  * Create a new Element
35367  * @param {Object} config The config object
35368  */
35369
35370 Roo.bootstrap.LayoutMasonryAuto = function(config){
35371     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35372 };
35373
35374 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35375     
35376       /**
35377      * @cfg {Boolean} isFitWidth  - resize the width..
35378      */   
35379     isFitWidth : false,  // options..
35380     /**
35381      * @cfg {Boolean} isOriginLeft = left align?
35382      */   
35383     isOriginLeft : true,
35384     /**
35385      * @cfg {Boolean} isOriginTop = top align?
35386      */   
35387     isOriginTop : false,
35388     /**
35389      * @cfg {Boolean} isLayoutInstant = no animation?
35390      */   
35391     isLayoutInstant : false, // needed?
35392     /**
35393      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35394      */   
35395     isResizingContainer : true,
35396     /**
35397      * @cfg {Number} columnWidth  width of the columns 
35398      */   
35399     
35400     columnWidth : 0,
35401     
35402     /**
35403      * @cfg {Number} maxCols maximum number of columns
35404      */   
35405     
35406     maxCols: 0,
35407     /**
35408      * @cfg {Number} padHeight padding below box..
35409      */   
35410     
35411     padHeight : 10, 
35412     
35413     /**
35414      * @cfg {Boolean} isAutoInitial defalut true
35415      */   
35416     
35417     isAutoInitial : true, 
35418     
35419     // private?
35420     gutter : 0,
35421     
35422     containerWidth: 0,
35423     initialColumnWidth : 0,
35424     currentSize : null,
35425     
35426     colYs : null, // array.
35427     maxY : 0,
35428     padWidth: 10,
35429     
35430     
35431     tag: 'div',
35432     cls: '',
35433     bricks: null, //CompositeElement
35434     cols : 0, // array?
35435     // element : null, // wrapped now this.el
35436     _isLayoutInited : null, 
35437     
35438     
35439     getAutoCreate : function(){
35440         
35441         var cfg = {
35442             tag: this.tag,
35443             cls: 'blog-masonary-wrapper ' + this.cls,
35444             cn : {
35445                 cls : 'mas-boxes masonary'
35446             }
35447         };
35448         
35449         return cfg;
35450     },
35451     
35452     getChildContainer: function( )
35453     {
35454         if (this.boxesEl) {
35455             return this.boxesEl;
35456         }
35457         
35458         this.boxesEl = this.el.select('.mas-boxes').first();
35459         
35460         return this.boxesEl;
35461     },
35462     
35463     
35464     initEvents : function()
35465     {
35466         var _this = this;
35467         
35468         if(this.isAutoInitial){
35469             Roo.log('hook children rendered');
35470             this.on('childrenrendered', function() {
35471                 Roo.log('children rendered');
35472                 _this.initial();
35473             } ,this);
35474         }
35475         
35476     },
35477     
35478     initial : function()
35479     {
35480         this.reloadItems();
35481
35482         this.currentSize = this.el.getBox(true);
35483
35484         /// was window resize... - let's see if this works..
35485         Roo.EventManager.onWindowResize(this.resize, this); 
35486
35487         if(!this.isAutoInitial){
35488             this.layout();
35489             return;
35490         }
35491         
35492         this.layout.defer(500,this);
35493     },
35494     
35495     reloadItems: function()
35496     {
35497         this.bricks = this.el.select('.masonry-brick', true);
35498         
35499         this.bricks.each(function(b) {
35500             //Roo.log(b.getSize());
35501             if (!b.attr('originalwidth')) {
35502                 b.attr('originalwidth',  b.getSize().width);
35503             }
35504             
35505         });
35506         
35507         Roo.log(this.bricks.elements.length);
35508     },
35509     
35510     resize : function()
35511     {
35512         Roo.log('resize');
35513         var cs = this.el.getBox(true);
35514         
35515         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35516             Roo.log("no change in with or X");
35517             return;
35518         }
35519         this.currentSize = cs;
35520         this.layout();
35521     },
35522     
35523     layout : function()
35524     {
35525          Roo.log('layout');
35526         this._resetLayout();
35527         //this._manageStamps();
35528       
35529         // don't animate first layout
35530         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35531         this.layoutItems( isInstant );
35532       
35533         // flag for initalized
35534         this._isLayoutInited = true;
35535     },
35536     
35537     layoutItems : function( isInstant )
35538     {
35539         //var items = this._getItemsForLayout( this.items );
35540         // original code supports filtering layout items.. we just ignore it..
35541         
35542         this._layoutItems( this.bricks , isInstant );
35543       
35544         this._postLayout();
35545     },
35546     _layoutItems : function ( items , isInstant)
35547     {
35548        //this.fireEvent( 'layout', this, items );
35549     
35550
35551         if ( !items || !items.elements.length ) {
35552           // no items, emit event with empty array
35553             return;
35554         }
35555
35556         var queue = [];
35557         items.each(function(item) {
35558             Roo.log("layout item");
35559             Roo.log(item);
35560             // get x/y object from method
35561             var position = this._getItemLayoutPosition( item );
35562             // enqueue
35563             position.item = item;
35564             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35565             queue.push( position );
35566         }, this);
35567       
35568         this._processLayoutQueue( queue );
35569     },
35570     /** Sets position of item in DOM
35571     * @param {Element} item
35572     * @param {Number} x - horizontal position
35573     * @param {Number} y - vertical position
35574     * @param {Boolean} isInstant - disables transitions
35575     */
35576     _processLayoutQueue : function( queue )
35577     {
35578         for ( var i=0, len = queue.length; i < len; i++ ) {
35579             var obj = queue[i];
35580             obj.item.position('absolute');
35581             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35582         }
35583     },
35584       
35585     
35586     /**
35587     * Any logic you want to do after each layout,
35588     * i.e. size the container
35589     */
35590     _postLayout : function()
35591     {
35592         this.resizeContainer();
35593     },
35594     
35595     resizeContainer : function()
35596     {
35597         if ( !this.isResizingContainer ) {
35598             return;
35599         }
35600         var size = this._getContainerSize();
35601         if ( size ) {
35602             this.el.setSize(size.width,size.height);
35603             this.boxesEl.setSize(size.width,size.height);
35604         }
35605     },
35606     
35607     
35608     
35609     _resetLayout : function()
35610     {
35611         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35612         this.colWidth = this.el.getWidth();
35613         //this.gutter = this.el.getWidth(); 
35614         
35615         this.measureColumns();
35616
35617         // reset column Y
35618         var i = this.cols;
35619         this.colYs = [];
35620         while (i--) {
35621             this.colYs.push( 0 );
35622         }
35623     
35624         this.maxY = 0;
35625     },
35626
35627     measureColumns : function()
35628     {
35629         this.getContainerWidth();
35630       // if columnWidth is 0, default to outerWidth of first item
35631         if ( !this.columnWidth ) {
35632             var firstItem = this.bricks.first();
35633             Roo.log(firstItem);
35634             this.columnWidth  = this.containerWidth;
35635             if (firstItem && firstItem.attr('originalwidth') ) {
35636                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35637             }
35638             // columnWidth fall back to item of first element
35639             Roo.log("set column width?");
35640                         this.initialColumnWidth = this.columnWidth  ;
35641
35642             // if first elem has no width, default to size of container
35643             
35644         }
35645         
35646         
35647         if (this.initialColumnWidth) {
35648             this.columnWidth = this.initialColumnWidth;
35649         }
35650         
35651         
35652             
35653         // column width is fixed at the top - however if container width get's smaller we should
35654         // reduce it...
35655         
35656         // this bit calcs how man columns..
35657             
35658         var columnWidth = this.columnWidth += this.gutter;
35659       
35660         // calculate columns
35661         var containerWidth = this.containerWidth + this.gutter;
35662         
35663         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35664         // fix rounding errors, typically with gutters
35665         var excess = columnWidth - containerWidth % columnWidth;
35666         
35667         
35668         // if overshoot is less than a pixel, round up, otherwise floor it
35669         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35670         cols = Math[ mathMethod ]( cols );
35671         this.cols = Math.max( cols, 1 );
35672         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35673         
35674          // padding positioning..
35675         var totalColWidth = this.cols * this.columnWidth;
35676         var padavail = this.containerWidth - totalColWidth;
35677         // so for 2 columns - we need 3 'pads'
35678         
35679         var padNeeded = (1+this.cols) * this.padWidth;
35680         
35681         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35682         
35683         this.columnWidth += padExtra
35684         //this.padWidth = Math.floor(padavail /  ( this.cols));
35685         
35686         // adjust colum width so that padding is fixed??
35687         
35688         // we have 3 columns ... total = width * 3
35689         // we have X left over... that should be used by 
35690         
35691         //if (this.expandC) {
35692             
35693         //}
35694         
35695         
35696         
35697     },
35698     
35699     getContainerWidth : function()
35700     {
35701        /* // container is parent if fit width
35702         var container = this.isFitWidth ? this.element.parentNode : this.element;
35703         // check that this.size and size are there
35704         // IE8 triggers resize on body size change, so they might not be
35705         
35706         var size = getSize( container );  //FIXME
35707         this.containerWidth = size && size.innerWidth; //FIXME
35708         */
35709          
35710         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35711         
35712     },
35713     
35714     _getItemLayoutPosition : function( item )  // what is item?
35715     {
35716         // we resize the item to our columnWidth..
35717       
35718         item.setWidth(this.columnWidth);
35719         item.autoBoxAdjust  = false;
35720         
35721         var sz = item.getSize();
35722  
35723         // how many columns does this brick span
35724         var remainder = this.containerWidth % this.columnWidth;
35725         
35726         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35727         // round if off by 1 pixel, otherwise use ceil
35728         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35729         colSpan = Math.min( colSpan, this.cols );
35730         
35731         // normally this should be '1' as we dont' currently allow multi width columns..
35732         
35733         var colGroup = this._getColGroup( colSpan );
35734         // get the minimum Y value from the columns
35735         var minimumY = Math.min.apply( Math, colGroup );
35736         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35737         
35738         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35739          
35740         // position the brick
35741         var position = {
35742             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35743             y: this.currentSize.y + minimumY + this.padHeight
35744         };
35745         
35746         Roo.log(position);
35747         // apply setHeight to necessary columns
35748         var setHeight = minimumY + sz.height + this.padHeight;
35749         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35750         
35751         var setSpan = this.cols + 1 - colGroup.length;
35752         for ( var i = 0; i < setSpan; i++ ) {
35753           this.colYs[ shortColIndex + i ] = setHeight ;
35754         }
35755       
35756         return position;
35757     },
35758     
35759     /**
35760      * @param {Number} colSpan - number of columns the element spans
35761      * @returns {Array} colGroup
35762      */
35763     _getColGroup : function( colSpan )
35764     {
35765         if ( colSpan < 2 ) {
35766           // if brick spans only one column, use all the column Ys
35767           return this.colYs;
35768         }
35769       
35770         var colGroup = [];
35771         // how many different places could this brick fit horizontally
35772         var groupCount = this.cols + 1 - colSpan;
35773         // for each group potential horizontal position
35774         for ( var i = 0; i < groupCount; i++ ) {
35775           // make an array of colY values for that one group
35776           var groupColYs = this.colYs.slice( i, i + colSpan );
35777           // and get the max value of the array
35778           colGroup[i] = Math.max.apply( Math, groupColYs );
35779         }
35780         return colGroup;
35781     },
35782     /*
35783     _manageStamp : function( stamp )
35784     {
35785         var stampSize =  stamp.getSize();
35786         var offset = stamp.getBox();
35787         // get the columns that this stamp affects
35788         var firstX = this.isOriginLeft ? offset.x : offset.right;
35789         var lastX = firstX + stampSize.width;
35790         var firstCol = Math.floor( firstX / this.columnWidth );
35791         firstCol = Math.max( 0, firstCol );
35792         
35793         var lastCol = Math.floor( lastX / this.columnWidth );
35794         // lastCol should not go over if multiple of columnWidth #425
35795         lastCol -= lastX % this.columnWidth ? 0 : 1;
35796         lastCol = Math.min( this.cols - 1, lastCol );
35797         
35798         // set colYs to bottom of the stamp
35799         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35800             stampSize.height;
35801             
35802         for ( var i = firstCol; i <= lastCol; i++ ) {
35803           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35804         }
35805     },
35806     */
35807     
35808     _getContainerSize : function()
35809     {
35810         this.maxY = Math.max.apply( Math, this.colYs );
35811         var size = {
35812             height: this.maxY
35813         };
35814       
35815         if ( this.isFitWidth ) {
35816             size.width = this._getContainerFitWidth();
35817         }
35818       
35819         return size;
35820     },
35821     
35822     _getContainerFitWidth : function()
35823     {
35824         var unusedCols = 0;
35825         // count unused columns
35826         var i = this.cols;
35827         while ( --i ) {
35828           if ( this.colYs[i] !== 0 ) {
35829             break;
35830           }
35831           unusedCols++;
35832         }
35833         // fit container to columns that have been used
35834         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35835     },
35836     
35837     needsResizeLayout : function()
35838     {
35839         var previousWidth = this.containerWidth;
35840         this.getContainerWidth();
35841         return previousWidth !== this.containerWidth;
35842     }
35843  
35844 });
35845
35846  
35847
35848  /*
35849  * - LGPL
35850  *
35851  * element
35852  * 
35853  */
35854
35855 /**
35856  * @class Roo.bootstrap.MasonryBrick
35857  * @extends Roo.bootstrap.Component
35858  * Bootstrap MasonryBrick class
35859  * 
35860  * @constructor
35861  * Create a new MasonryBrick
35862  * @param {Object} config The config object
35863  */
35864
35865 Roo.bootstrap.MasonryBrick = function(config){
35866     
35867     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35868     
35869     Roo.bootstrap.MasonryBrick.register(this);
35870     
35871     this.addEvents({
35872         // raw events
35873         /**
35874          * @event click
35875          * When a MasonryBrick is clcik
35876          * @param {Roo.bootstrap.MasonryBrick} this
35877          * @param {Roo.EventObject} e
35878          */
35879         "click" : true
35880     });
35881 };
35882
35883 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35884     
35885     /**
35886      * @cfg {String} title
35887      */   
35888     title : '',
35889     /**
35890      * @cfg {String} html
35891      */   
35892     html : '',
35893     /**
35894      * @cfg {String} bgimage
35895      */   
35896     bgimage : '',
35897     /**
35898      * @cfg {String} videourl
35899      */   
35900     videourl : '',
35901     /**
35902      * @cfg {String} cls
35903      */   
35904     cls : '',
35905     /**
35906      * @cfg {String} href
35907      */   
35908     href : '',
35909     /**
35910      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35911      */   
35912     size : 'xs',
35913     
35914     /**
35915      * @cfg {String} placetitle (center|bottom)
35916      */   
35917     placetitle : '',
35918     
35919     /**
35920      * @cfg {Boolean} isFitContainer defalut true
35921      */   
35922     isFitContainer : true, 
35923     
35924     /**
35925      * @cfg {Boolean} preventDefault defalut false
35926      */   
35927     preventDefault : false, 
35928     
35929     /**
35930      * @cfg {Boolean} inverse defalut false
35931      */   
35932     maskInverse : false, 
35933     
35934     getAutoCreate : function()
35935     {
35936         if(!this.isFitContainer){
35937             return this.getSplitAutoCreate();
35938         }
35939         
35940         var cls = 'masonry-brick masonry-brick-full';
35941         
35942         if(this.href.length){
35943             cls += ' masonry-brick-link';
35944         }
35945         
35946         if(this.bgimage.length){
35947             cls += ' masonry-brick-image';
35948         }
35949         
35950         if(this.maskInverse){
35951             cls += ' mask-inverse';
35952         }
35953         
35954         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35955             cls += ' enable-mask';
35956         }
35957         
35958         if(this.size){
35959             cls += ' masonry-' + this.size + '-brick';
35960         }
35961         
35962         if(this.placetitle.length){
35963             
35964             switch (this.placetitle) {
35965                 case 'center' :
35966                     cls += ' masonry-center-title';
35967                     break;
35968                 case 'bottom' :
35969                     cls += ' masonry-bottom-title';
35970                     break;
35971                 default:
35972                     break;
35973             }
35974             
35975         } else {
35976             if(!this.html.length && !this.bgimage.length){
35977                 cls += ' masonry-center-title';
35978             }
35979
35980             if(!this.html.length && this.bgimage.length){
35981                 cls += ' masonry-bottom-title';
35982             }
35983         }
35984         
35985         if(this.cls){
35986             cls += ' ' + this.cls;
35987         }
35988         
35989         var cfg = {
35990             tag: (this.href.length) ? 'a' : 'div',
35991             cls: cls,
35992             cn: [
35993                 {
35994                     tag: 'div',
35995                     cls: 'masonry-brick-mask'
35996                 },
35997                 {
35998                     tag: 'div',
35999                     cls: 'masonry-brick-paragraph',
36000                     cn: []
36001                 }
36002             ]
36003         };
36004         
36005         if(this.href.length){
36006             cfg.href = this.href;
36007         }
36008         
36009         var cn = cfg.cn[1].cn;
36010         
36011         if(this.title.length){
36012             cn.push({
36013                 tag: 'h4',
36014                 cls: 'masonry-brick-title',
36015                 html: this.title
36016             });
36017         }
36018         
36019         if(this.html.length){
36020             cn.push({
36021                 tag: 'p',
36022                 cls: 'masonry-brick-text',
36023                 html: this.html
36024             });
36025         }
36026         
36027         if (!this.title.length && !this.html.length) {
36028             cfg.cn[1].cls += ' hide';
36029         }
36030         
36031         if(this.bgimage.length){
36032             cfg.cn.push({
36033                 tag: 'img',
36034                 cls: 'masonry-brick-image-view',
36035                 src: this.bgimage
36036             });
36037         }
36038         
36039         if(this.videourl.length){
36040             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36041             // youtube support only?
36042             cfg.cn.push({
36043                 tag: 'iframe',
36044                 cls: 'masonry-brick-image-view',
36045                 src: vurl,
36046                 frameborder : 0,
36047                 allowfullscreen : true
36048             });
36049         }
36050         
36051         return cfg;
36052         
36053     },
36054     
36055     getSplitAutoCreate : function()
36056     {
36057         var cls = 'masonry-brick masonry-brick-split';
36058         
36059         if(this.href.length){
36060             cls += ' masonry-brick-link';
36061         }
36062         
36063         if(this.bgimage.length){
36064             cls += ' masonry-brick-image';
36065         }
36066         
36067         if(this.size){
36068             cls += ' masonry-' + this.size + '-brick';
36069         }
36070         
36071         switch (this.placetitle) {
36072             case 'center' :
36073                 cls += ' masonry-center-title';
36074                 break;
36075             case 'bottom' :
36076                 cls += ' masonry-bottom-title';
36077                 break;
36078             default:
36079                 if(!this.bgimage.length){
36080                     cls += ' masonry-center-title';
36081                 }
36082
36083                 if(this.bgimage.length){
36084                     cls += ' masonry-bottom-title';
36085                 }
36086                 break;
36087         }
36088         
36089         if(this.cls){
36090             cls += ' ' + this.cls;
36091         }
36092         
36093         var cfg = {
36094             tag: (this.href.length) ? 'a' : 'div',
36095             cls: cls,
36096             cn: [
36097                 {
36098                     tag: 'div',
36099                     cls: 'masonry-brick-split-head',
36100                     cn: [
36101                         {
36102                             tag: 'div',
36103                             cls: 'masonry-brick-paragraph',
36104                             cn: []
36105                         }
36106                     ]
36107                 },
36108                 {
36109                     tag: 'div',
36110                     cls: 'masonry-brick-split-body',
36111                     cn: []
36112                 }
36113             ]
36114         };
36115         
36116         if(this.href.length){
36117             cfg.href = this.href;
36118         }
36119         
36120         if(this.title.length){
36121             cfg.cn[0].cn[0].cn.push({
36122                 tag: 'h4',
36123                 cls: 'masonry-brick-title',
36124                 html: this.title
36125             });
36126         }
36127         
36128         if(this.html.length){
36129             cfg.cn[1].cn.push({
36130                 tag: 'p',
36131                 cls: 'masonry-brick-text',
36132                 html: this.html
36133             });
36134         }
36135
36136         if(this.bgimage.length){
36137             cfg.cn[0].cn.push({
36138                 tag: 'img',
36139                 cls: 'masonry-brick-image-view',
36140                 src: this.bgimage
36141             });
36142         }
36143         
36144         if(this.videourl.length){
36145             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36146             // youtube support only?
36147             cfg.cn[0].cn.cn.push({
36148                 tag: 'iframe',
36149                 cls: 'masonry-brick-image-view',
36150                 src: vurl,
36151                 frameborder : 0,
36152                 allowfullscreen : true
36153             });
36154         }
36155         
36156         return cfg;
36157     },
36158     
36159     initEvents: function() 
36160     {
36161         switch (this.size) {
36162             case 'xs' :
36163                 this.x = 1;
36164                 this.y = 1;
36165                 break;
36166             case 'sm' :
36167                 this.x = 2;
36168                 this.y = 2;
36169                 break;
36170             case 'md' :
36171             case 'md-left' :
36172             case 'md-right' :
36173                 this.x = 3;
36174                 this.y = 3;
36175                 break;
36176             case 'tall' :
36177                 this.x = 2;
36178                 this.y = 3;
36179                 break;
36180             case 'wide' :
36181                 this.x = 3;
36182                 this.y = 2;
36183                 break;
36184             case 'wide-thin' :
36185                 this.x = 3;
36186                 this.y = 1;
36187                 break;
36188                         
36189             default :
36190                 break;
36191         }
36192         
36193         if(Roo.isTouch){
36194             this.el.on('touchstart', this.onTouchStart, this);
36195             this.el.on('touchmove', this.onTouchMove, this);
36196             this.el.on('touchend', this.onTouchEnd, this);
36197             this.el.on('contextmenu', this.onContextMenu, this);
36198         } else {
36199             this.el.on('mouseenter'  ,this.enter, this);
36200             this.el.on('mouseleave', this.leave, this);
36201             this.el.on('click', this.onClick, this);
36202         }
36203         
36204         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36205             this.parent().bricks.push(this);   
36206         }
36207         
36208     },
36209     
36210     onClick: function(e, el)
36211     {
36212         var time = this.endTimer - this.startTimer;
36213         // Roo.log(e.preventDefault());
36214         if(Roo.isTouch){
36215             if(time > 1000){
36216                 e.preventDefault();
36217                 return;
36218             }
36219         }
36220         
36221         if(!this.preventDefault){
36222             return;
36223         }
36224         
36225         e.preventDefault();
36226         
36227         if (this.activeClass != '') {
36228             this.selectBrick();
36229         }
36230         
36231         this.fireEvent('click', this, e);
36232     },
36233     
36234     enter: function(e, el)
36235     {
36236         e.preventDefault();
36237         
36238         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36239             return;
36240         }
36241         
36242         if(this.bgimage.length && this.html.length){
36243             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36244         }
36245     },
36246     
36247     leave: function(e, el)
36248     {
36249         e.preventDefault();
36250         
36251         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36252             return;
36253         }
36254         
36255         if(this.bgimage.length && this.html.length){
36256             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36257         }
36258     },
36259     
36260     onTouchStart: function(e, el)
36261     {
36262 //        e.preventDefault();
36263         
36264         this.touchmoved = false;
36265         
36266         if(!this.isFitContainer){
36267             return;
36268         }
36269         
36270         if(!this.bgimage.length || !this.html.length){
36271             return;
36272         }
36273         
36274         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36275         
36276         this.timer = new Date().getTime();
36277         
36278     },
36279     
36280     onTouchMove: function(e, el)
36281     {
36282         this.touchmoved = true;
36283     },
36284     
36285     onContextMenu : function(e,el)
36286     {
36287         e.preventDefault();
36288         e.stopPropagation();
36289         return false;
36290     },
36291     
36292     onTouchEnd: function(e, el)
36293     {
36294 //        e.preventDefault();
36295         
36296         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36297         
36298             this.leave(e,el);
36299             
36300             return;
36301         }
36302         
36303         if(!this.bgimage.length || !this.html.length){
36304             
36305             if(this.href.length){
36306                 window.location.href = this.href;
36307             }
36308             
36309             return;
36310         }
36311         
36312         if(!this.isFitContainer){
36313             return;
36314         }
36315         
36316         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36317         
36318         window.location.href = this.href;
36319     },
36320     
36321     //selection on single brick only
36322     selectBrick : function() {
36323         
36324         if (!this.parentId) {
36325             return;
36326         }
36327         
36328         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36329         var index = m.selectedBrick.indexOf(this.id);
36330         
36331         if ( index > -1) {
36332             m.selectedBrick.splice(index,1);
36333             this.el.removeClass(this.activeClass);
36334             return;
36335         }
36336         
36337         for(var i = 0; i < m.selectedBrick.length; i++) {
36338             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36339             b.el.removeClass(b.activeClass);
36340         }
36341         
36342         m.selectedBrick = [];
36343         
36344         m.selectedBrick.push(this.id);
36345         this.el.addClass(this.activeClass);
36346         return;
36347     },
36348     
36349     isSelected : function(){
36350         return this.el.hasClass(this.activeClass);
36351         
36352     }
36353 });
36354
36355 Roo.apply(Roo.bootstrap.MasonryBrick, {
36356     
36357     //groups: {},
36358     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36359      /**
36360     * register a Masonry Brick
36361     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36362     */
36363     
36364     register : function(brick)
36365     {
36366         //this.groups[brick.id] = brick;
36367         this.groups.add(brick.id, brick);
36368     },
36369     /**
36370     * fetch a  masonry brick based on the masonry brick ID
36371     * @param {string} the masonry brick to add
36372     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36373     */
36374     
36375     get: function(brick_id) 
36376     {
36377         // if (typeof(this.groups[brick_id]) == 'undefined') {
36378         //     return false;
36379         // }
36380         // return this.groups[brick_id] ;
36381         
36382         if(this.groups.key(brick_id)) {
36383             return this.groups.key(brick_id);
36384         }
36385         
36386         return false;
36387     }
36388     
36389     
36390     
36391 });
36392
36393  /*
36394  * - LGPL
36395  *
36396  * element
36397  * 
36398  */
36399
36400 /**
36401  * @class Roo.bootstrap.Brick
36402  * @extends Roo.bootstrap.Component
36403  * Bootstrap Brick class
36404  * 
36405  * @constructor
36406  * Create a new Brick
36407  * @param {Object} config The config object
36408  */
36409
36410 Roo.bootstrap.Brick = function(config){
36411     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36412     
36413     this.addEvents({
36414         // raw events
36415         /**
36416          * @event click
36417          * When a Brick is click
36418          * @param {Roo.bootstrap.Brick} this
36419          * @param {Roo.EventObject} e
36420          */
36421         "click" : true
36422     });
36423 };
36424
36425 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36426     
36427     /**
36428      * @cfg {String} title
36429      */   
36430     title : '',
36431     /**
36432      * @cfg {String} html
36433      */   
36434     html : '',
36435     /**
36436      * @cfg {String} bgimage
36437      */   
36438     bgimage : '',
36439     /**
36440      * @cfg {String} cls
36441      */   
36442     cls : '',
36443     /**
36444      * @cfg {String} href
36445      */   
36446     href : '',
36447     /**
36448      * @cfg {String} video
36449      */   
36450     video : '',
36451     /**
36452      * @cfg {Boolean} square
36453      */   
36454     square : true,
36455     
36456     getAutoCreate : function()
36457     {
36458         var cls = 'roo-brick';
36459         
36460         if(this.href.length){
36461             cls += ' roo-brick-link';
36462         }
36463         
36464         if(this.bgimage.length){
36465             cls += ' roo-brick-image';
36466         }
36467         
36468         if(!this.html.length && !this.bgimage.length){
36469             cls += ' roo-brick-center-title';
36470         }
36471         
36472         if(!this.html.length && this.bgimage.length){
36473             cls += ' roo-brick-bottom-title';
36474         }
36475         
36476         if(this.cls){
36477             cls += ' ' + this.cls;
36478         }
36479         
36480         var cfg = {
36481             tag: (this.href.length) ? 'a' : 'div',
36482             cls: cls,
36483             cn: [
36484                 {
36485                     tag: 'div',
36486                     cls: 'roo-brick-paragraph',
36487                     cn: []
36488                 }
36489             ]
36490         };
36491         
36492         if(this.href.length){
36493             cfg.href = this.href;
36494         }
36495         
36496         var cn = cfg.cn[0].cn;
36497         
36498         if(this.title.length){
36499             cn.push({
36500                 tag: 'h4',
36501                 cls: 'roo-brick-title',
36502                 html: this.title
36503             });
36504         }
36505         
36506         if(this.html.length){
36507             cn.push({
36508                 tag: 'p',
36509                 cls: 'roo-brick-text',
36510                 html: this.html
36511             });
36512         } else {
36513             cn.cls += ' hide';
36514         }
36515         
36516         if(this.bgimage.length){
36517             cfg.cn.push({
36518                 tag: 'img',
36519                 cls: 'roo-brick-image-view',
36520                 src: this.bgimage
36521             });
36522         }
36523         
36524         return cfg;
36525     },
36526     
36527     initEvents: function() 
36528     {
36529         if(this.title.length || this.html.length){
36530             this.el.on('mouseenter'  ,this.enter, this);
36531             this.el.on('mouseleave', this.leave, this);
36532         }
36533         
36534         Roo.EventManager.onWindowResize(this.resize, this); 
36535         
36536         if(this.bgimage.length){
36537             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36538             this.imageEl.on('load', this.onImageLoad, this);
36539             return;
36540         }
36541         
36542         this.resize();
36543     },
36544     
36545     onImageLoad : function()
36546     {
36547         this.resize();
36548     },
36549     
36550     resize : function()
36551     {
36552         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36553         
36554         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36555         
36556         if(this.bgimage.length){
36557             var image = this.el.select('.roo-brick-image-view', true).first();
36558             
36559             image.setWidth(paragraph.getWidth());
36560             
36561             if(this.square){
36562                 image.setHeight(paragraph.getWidth());
36563             }
36564             
36565             this.el.setHeight(image.getHeight());
36566             paragraph.setHeight(image.getHeight());
36567             
36568         }
36569         
36570     },
36571     
36572     enter: function(e, el)
36573     {
36574         e.preventDefault();
36575         
36576         if(this.bgimage.length){
36577             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36578             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36579         }
36580     },
36581     
36582     leave: function(e, el)
36583     {
36584         e.preventDefault();
36585         
36586         if(this.bgimage.length){
36587             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36588             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36589         }
36590     }
36591     
36592 });
36593
36594  
36595
36596  /*
36597  * - LGPL
36598  *
36599  * Number field 
36600  */
36601
36602 /**
36603  * @class Roo.bootstrap.form.NumberField
36604  * @extends Roo.bootstrap.form.Input
36605  * Bootstrap NumberField class
36606  * 
36607  * 
36608  * 
36609  * 
36610  * @constructor
36611  * Create a new NumberField
36612  * @param {Object} config The config object
36613  */
36614
36615 Roo.bootstrap.form.NumberField = function(config){
36616     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36617 };
36618
36619 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36620     
36621     /**
36622      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36623      */
36624     allowDecimals : true,
36625     /**
36626      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36627      */
36628     decimalSeparator : ".",
36629     /**
36630      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36631      */
36632     decimalPrecision : 2,
36633     /**
36634      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36635      */
36636     allowNegative : true,
36637     
36638     /**
36639      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36640      */
36641     allowZero: true,
36642     /**
36643      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36644      */
36645     minValue : Number.NEGATIVE_INFINITY,
36646     /**
36647      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36648      */
36649     maxValue : Number.MAX_VALUE,
36650     /**
36651      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36652      */
36653     minText : "The minimum value for this field is {0}",
36654     /**
36655      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36656      */
36657     maxText : "The maximum value for this field is {0}",
36658     /**
36659      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36660      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36661      */
36662     nanText : "{0} is not a valid number",
36663     /**
36664      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36665      */
36666     thousandsDelimiter : false,
36667     /**
36668      * @cfg {String} valueAlign alignment of value
36669      */
36670     valueAlign : "left",
36671
36672     getAutoCreate : function()
36673     {
36674         var hiddenInput = {
36675             tag: 'input',
36676             type: 'hidden',
36677             id: Roo.id(),
36678             cls: 'hidden-number-input'
36679         };
36680         
36681         if (this.name) {
36682             hiddenInput.name = this.name;
36683         }
36684         
36685         this.name = '';
36686         
36687         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36688         
36689         this.name = hiddenInput.name;
36690         
36691         if(cfg.cn.length > 0) {
36692             cfg.cn.push(hiddenInput);
36693         }
36694         
36695         return cfg;
36696     },
36697
36698     // private
36699     initEvents : function()
36700     {   
36701         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36702         
36703         var allowed = "0123456789";
36704         
36705         if(this.allowDecimals){
36706             allowed += this.decimalSeparator;
36707         }
36708         
36709         if(this.allowNegative){
36710             allowed += "-";
36711         }
36712         
36713         if(this.thousandsDelimiter) {
36714             allowed += ",";
36715         }
36716         
36717         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36718         
36719         var keyPress = function(e){
36720             
36721             var k = e.getKey();
36722             
36723             var c = e.getCharCode();
36724             
36725             if(
36726                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36727                     allowed.indexOf(String.fromCharCode(c)) === -1
36728             ){
36729                 e.stopEvent();
36730                 return;
36731             }
36732             
36733             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36734                 return;
36735             }
36736             
36737             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36738                 e.stopEvent();
36739             }
36740         };
36741         
36742         this.el.on("keypress", keyPress, this);
36743     },
36744     
36745     validateValue : function(value)
36746     {
36747         
36748         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36749             return false;
36750         }
36751         
36752         var num = this.parseValue(value);
36753         
36754         if(isNaN(num)){
36755             this.markInvalid(String.format(this.nanText, value));
36756             return false;
36757         }
36758         
36759         if(num < this.minValue){
36760             this.markInvalid(String.format(this.minText, this.minValue));
36761             return false;
36762         }
36763         
36764         if(num > this.maxValue){
36765             this.markInvalid(String.format(this.maxText, this.maxValue));
36766             return false;
36767         }
36768         
36769         return true;
36770     },
36771
36772     getValue : function()
36773     {
36774         var v = this.hiddenEl().getValue();
36775         
36776         return this.fixPrecision(this.parseValue(v));
36777     },
36778
36779     parseValue : function(value)
36780     {
36781         if(this.thousandsDelimiter) {
36782             value += "";
36783             r = new RegExp(",", "g");
36784             value = value.replace(r, "");
36785         }
36786         
36787         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36788         return isNaN(value) ? '' : value;
36789     },
36790
36791     fixPrecision : function(value)
36792     {
36793         if(this.thousandsDelimiter) {
36794             value += "";
36795             r = new RegExp(",", "g");
36796             value = value.replace(r, "");
36797         }
36798         
36799         var nan = isNaN(value);
36800         
36801         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36802             return nan ? '' : value;
36803         }
36804         return parseFloat(value).toFixed(this.decimalPrecision);
36805     },
36806
36807     setValue : function(v)
36808     {
36809         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36810         
36811         this.value = v;
36812         
36813         if(this.rendered){
36814             
36815             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36816             
36817             this.inputEl().dom.value = (v == '') ? '' :
36818                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36819             
36820             if(!this.allowZero && v === '0') {
36821                 this.hiddenEl().dom.value = '';
36822                 this.inputEl().dom.value = '';
36823             }
36824             
36825             this.validate();
36826         }
36827     },
36828
36829     decimalPrecisionFcn : function(v)
36830     {
36831         return Math.floor(v);
36832     },
36833
36834     beforeBlur : function()
36835     {
36836         var v = this.parseValue(this.getRawValue());
36837         
36838         if(v || v === 0 || v === ''){
36839             this.setValue(v);
36840         }
36841     },
36842     
36843     hiddenEl : function()
36844     {
36845         return this.el.select('input.hidden-number-input',true).first();
36846     }
36847     
36848 });
36849
36850  
36851
36852 /*
36853 * Licence: LGPL
36854 */
36855
36856 /**
36857  * @class Roo.bootstrap.DocumentSlider
36858  * @extends Roo.bootstrap.Component
36859  * Bootstrap DocumentSlider class
36860  * 
36861  * @constructor
36862  * Create a new DocumentViewer
36863  * @param {Object} config The config object
36864  */
36865
36866 Roo.bootstrap.DocumentSlider = function(config){
36867     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36868     
36869     this.files = [];
36870     
36871     this.addEvents({
36872         /**
36873          * @event initial
36874          * Fire after initEvent
36875          * @param {Roo.bootstrap.DocumentSlider} this
36876          */
36877         "initial" : true,
36878         /**
36879          * @event update
36880          * Fire after update
36881          * @param {Roo.bootstrap.DocumentSlider} this
36882          */
36883         "update" : true,
36884         /**
36885          * @event click
36886          * Fire after click
36887          * @param {Roo.bootstrap.DocumentSlider} this
36888          */
36889         "click" : true
36890     });
36891 };
36892
36893 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36894     
36895     files : false,
36896     
36897     indicator : 0,
36898     
36899     getAutoCreate : function()
36900     {
36901         var cfg = {
36902             tag : 'div',
36903             cls : 'roo-document-slider',
36904             cn : [
36905                 {
36906                     tag : 'div',
36907                     cls : 'roo-document-slider-header',
36908                     cn : [
36909                         {
36910                             tag : 'div',
36911                             cls : 'roo-document-slider-header-title'
36912                         }
36913                     ]
36914                 },
36915                 {
36916                     tag : 'div',
36917                     cls : 'roo-document-slider-body',
36918                     cn : [
36919                         {
36920                             tag : 'div',
36921                             cls : 'roo-document-slider-prev',
36922                             cn : [
36923                                 {
36924                                     tag : 'i',
36925                                     cls : 'fa fa-chevron-left'
36926                                 }
36927                             ]
36928                         },
36929                         {
36930                             tag : 'div',
36931                             cls : 'roo-document-slider-thumb',
36932                             cn : [
36933                                 {
36934                                     tag : 'img',
36935                                     cls : 'roo-document-slider-image'
36936                                 }
36937                             ]
36938                         },
36939                         {
36940                             tag : 'div',
36941                             cls : 'roo-document-slider-next',
36942                             cn : [
36943                                 {
36944                                     tag : 'i',
36945                                     cls : 'fa fa-chevron-right'
36946                                 }
36947                             ]
36948                         }
36949                     ]
36950                 }
36951             ]
36952         };
36953         
36954         return cfg;
36955     },
36956     
36957     initEvents : function()
36958     {
36959         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36960         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36961         
36962         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36963         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36964         
36965         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36966         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36967         
36968         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36969         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36970         
36971         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36972         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36973         
36974         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36975         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36976         
36977         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36978         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36979         
36980         this.thumbEl.on('click', this.onClick, this);
36981         
36982         this.prevIndicator.on('click', this.prev, this);
36983         
36984         this.nextIndicator.on('click', this.next, this);
36985         
36986     },
36987     
36988     initial : function()
36989     {
36990         if(this.files.length){
36991             this.indicator = 1;
36992             this.update()
36993         }
36994         
36995         this.fireEvent('initial', this);
36996     },
36997     
36998     update : function()
36999     {
37000         this.imageEl.attr('src', this.files[this.indicator - 1]);
37001         
37002         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37003         
37004         this.prevIndicator.show();
37005         
37006         if(this.indicator == 1){
37007             this.prevIndicator.hide();
37008         }
37009         
37010         this.nextIndicator.show();
37011         
37012         if(this.indicator == this.files.length){
37013             this.nextIndicator.hide();
37014         }
37015         
37016         this.thumbEl.scrollTo('top');
37017         
37018         this.fireEvent('update', this);
37019     },
37020     
37021     onClick : function(e)
37022     {
37023         e.preventDefault();
37024         
37025         this.fireEvent('click', this);
37026     },
37027     
37028     prev : function(e)
37029     {
37030         e.preventDefault();
37031         
37032         this.indicator = Math.max(1, this.indicator - 1);
37033         
37034         this.update();
37035     },
37036     
37037     next : function(e)
37038     {
37039         e.preventDefault();
37040         
37041         this.indicator = Math.min(this.files.length, this.indicator + 1);
37042         
37043         this.update();
37044     }
37045 });
37046 /*
37047  * - LGPL
37048  *
37049  * RadioSet
37050  *
37051  *
37052  */
37053
37054 /**
37055  * @class Roo.bootstrap.form.RadioSet
37056  * @extends Roo.bootstrap.form.Input
37057  * @children Roo.bootstrap.form.Radio
37058  * Bootstrap RadioSet class
37059  * @cfg {String} indicatorpos (left|right) default left
37060  * @cfg {Boolean} inline (true|false) inline the element (default true)
37061  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37062  * @constructor
37063  * Create a new RadioSet
37064  * @param {Object} config The config object
37065  */
37066
37067 Roo.bootstrap.form.RadioSet = function(config){
37068     
37069     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37070     
37071     this.radioes = [];
37072     
37073     Roo.bootstrap.form.RadioSet.register(this);
37074     
37075     this.addEvents({
37076         /**
37077         * @event check
37078         * Fires when the element is checked or unchecked.
37079         * @param {Roo.bootstrap.form.RadioSet} this This radio
37080         * @param {Roo.bootstrap.form.Radio} item The checked item
37081         */
37082        check : true,
37083        /**
37084         * @event click
37085         * Fires when the element is click.
37086         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37087         * @param {Roo.bootstrap.form.Radio} item The checked item
37088         * @param {Roo.EventObject} e The event object
37089         */
37090        click : true
37091     });
37092     
37093 };
37094
37095 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37096
37097     radioes : false,
37098     
37099     inline : true,
37100     
37101     weight : '',
37102     
37103     indicatorpos : 'left',
37104     
37105     getAutoCreate : function()
37106     {
37107         var label = {
37108             tag : 'label',
37109             cls : 'roo-radio-set-label',
37110             cn : [
37111                 {
37112                     tag : 'span',
37113                     html : this.fieldLabel
37114                 }
37115             ]
37116         };
37117         if (Roo.bootstrap.version == 3) {
37118             
37119             
37120             if(this.indicatorpos == 'left'){
37121                 label.cn.unshift({
37122                     tag : 'i',
37123                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37124                     tooltip : 'This field is required'
37125                 });
37126             } else {
37127                 label.cn.push({
37128                     tag : 'i',
37129                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37130                     tooltip : 'This field is required'
37131                 });
37132             }
37133         }
37134         var items = {
37135             tag : 'div',
37136             cls : 'roo-radio-set-items'
37137         };
37138         
37139         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37140         
37141         if (align === 'left' && this.fieldLabel.length) {
37142             
37143             items = {
37144                 cls : "roo-radio-set-right", 
37145                 cn: [
37146                     items
37147                 ]
37148             };
37149             
37150             if(this.labelWidth > 12){
37151                 label.style = "width: " + this.labelWidth + 'px';
37152             }
37153             
37154             if(this.labelWidth < 13 && this.labelmd == 0){
37155                 this.labelmd = this.labelWidth;
37156             }
37157             
37158             if(this.labellg > 0){
37159                 label.cls += ' col-lg-' + this.labellg;
37160                 items.cls += ' col-lg-' + (12 - this.labellg);
37161             }
37162             
37163             if(this.labelmd > 0){
37164                 label.cls += ' col-md-' + this.labelmd;
37165                 items.cls += ' col-md-' + (12 - this.labelmd);
37166             }
37167             
37168             if(this.labelsm > 0){
37169                 label.cls += ' col-sm-' + this.labelsm;
37170                 items.cls += ' col-sm-' + (12 - this.labelsm);
37171             }
37172             
37173             if(this.labelxs > 0){
37174                 label.cls += ' col-xs-' + this.labelxs;
37175                 items.cls += ' col-xs-' + (12 - this.labelxs);
37176             }
37177         }
37178         
37179         var cfg = {
37180             tag : 'div',
37181             cls : 'roo-radio-set',
37182             cn : [
37183                 {
37184                     tag : 'input',
37185                     cls : 'roo-radio-set-input',
37186                     type : 'hidden',
37187                     name : this.name,
37188                     value : this.value ? this.value :  ''
37189                 },
37190                 label,
37191                 items
37192             ]
37193         };
37194         
37195         if(this.weight.length){
37196             cfg.cls += ' roo-radio-' + this.weight;
37197         }
37198         
37199         if(this.inline) {
37200             cfg.cls += ' roo-radio-set-inline';
37201         }
37202         
37203         var settings=this;
37204         ['xs','sm','md','lg'].map(function(size){
37205             if (settings[size]) {
37206                 cfg.cls += ' col-' + size + '-' + settings[size];
37207             }
37208         });
37209         
37210         return cfg;
37211         
37212     },
37213
37214     initEvents : function()
37215     {
37216         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37217         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37218         
37219         if(!this.fieldLabel.length){
37220             this.labelEl.hide();
37221         }
37222         
37223         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37224         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37225         
37226         this.indicator = this.indicatorEl();
37227         
37228         if(this.indicator){
37229             this.indicator.addClass('invisible');
37230         }
37231         
37232         this.originalValue = this.getValue();
37233         
37234     },
37235     
37236     inputEl: function ()
37237     {
37238         return this.el.select('.roo-radio-set-input', true).first();
37239     },
37240     
37241     getChildContainer : function()
37242     {
37243         return this.itemsEl;
37244     },
37245     
37246     register : function(item)
37247     {
37248         this.radioes.push(item);
37249         
37250     },
37251     
37252     validate : function()
37253     {   
37254         if(this.getVisibilityEl().hasClass('hidden')){
37255             return true;
37256         }
37257         
37258         var valid = false;
37259         
37260         Roo.each(this.radioes, function(i){
37261             if(!i.checked){
37262                 return;
37263             }
37264             
37265             valid = true;
37266             return false;
37267         });
37268         
37269         if(this.allowBlank) {
37270             return true;
37271         }
37272         
37273         if(this.disabled || valid){
37274             this.markValid();
37275             return true;
37276         }
37277         
37278         this.markInvalid();
37279         return false;
37280         
37281     },
37282     
37283     markValid : function()
37284     {
37285         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37286             this.indicatorEl().removeClass('visible');
37287             this.indicatorEl().addClass('invisible');
37288         }
37289         
37290         
37291         if (Roo.bootstrap.version == 3) {
37292             this.el.removeClass([this.invalidClass, this.validClass]);
37293             this.el.addClass(this.validClass);
37294         } else {
37295             this.el.removeClass(['is-invalid','is-valid']);
37296             this.el.addClass(['is-valid']);
37297         }
37298         this.fireEvent('valid', this);
37299     },
37300     
37301     markInvalid : function(msg)
37302     {
37303         if(this.allowBlank || this.disabled){
37304             return;
37305         }
37306         
37307         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37308             this.indicatorEl().removeClass('invisible');
37309             this.indicatorEl().addClass('visible');
37310         }
37311         if (Roo.bootstrap.version == 3) {
37312             this.el.removeClass([this.invalidClass, this.validClass]);
37313             this.el.addClass(this.invalidClass);
37314         } else {
37315             this.el.removeClass(['is-invalid','is-valid']);
37316             this.el.addClass(['is-invalid']);
37317         }
37318         
37319         this.fireEvent('invalid', this, msg);
37320         
37321     },
37322     
37323     setValue : function(v, suppressEvent)
37324     {   
37325         if(this.value === v){
37326             return;
37327         }
37328         
37329         this.value = v;
37330         
37331         if(this.rendered){
37332             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37333         }
37334         
37335         Roo.each(this.radioes, function(i){
37336             i.checked = false;
37337             i.el.removeClass('checked');
37338         });
37339         
37340         Roo.each(this.radioes, function(i){
37341             
37342             if(i.value === v || i.value.toString() === v.toString()){
37343                 i.checked = true;
37344                 i.el.addClass('checked');
37345                 
37346                 if(suppressEvent !== true){
37347                     this.fireEvent('check', this, i);
37348                 }
37349                 
37350                 return false;
37351             }
37352             
37353         }, this);
37354         
37355         this.validate();
37356     },
37357     
37358     clearInvalid : function(){
37359         
37360         if(!this.el || this.preventMark){
37361             return;
37362         }
37363         
37364         this.el.removeClass([this.invalidClass]);
37365         
37366         this.fireEvent('valid', this);
37367     }
37368     
37369 });
37370
37371 Roo.apply(Roo.bootstrap.form.RadioSet, {
37372     
37373     groups: {},
37374     
37375     register : function(set)
37376     {
37377         this.groups[set.name] = set;
37378     },
37379     
37380     get: function(name) 
37381     {
37382         if (typeof(this.groups[name]) == 'undefined') {
37383             return false;
37384         }
37385         
37386         return this.groups[name] ;
37387     }
37388     
37389 });
37390 /*
37391  * Based on:
37392  * Ext JS Library 1.1.1
37393  * Copyright(c) 2006-2007, Ext JS, LLC.
37394  *
37395  * Originally Released Under LGPL - original licence link has changed is not relivant.
37396  *
37397  * Fork - LGPL
37398  * <script type="text/javascript">
37399  */
37400
37401
37402 /**
37403  * @class Roo.bootstrap.SplitBar
37404  * @extends Roo.util.Observable
37405  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37406  * <br><br>
37407  * Usage:
37408  * <pre><code>
37409 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37410                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37411 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37412 split.minSize = 100;
37413 split.maxSize = 600;
37414 split.animate = true;
37415 split.on('moved', splitterMoved);
37416 </code></pre>
37417  * @constructor
37418  * Create a new SplitBar
37419  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37420  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37421  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37422  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37423                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37424                         position of the SplitBar).
37425  */
37426 Roo.bootstrap.SplitBar = function(cfg){
37427     
37428     /** @private */
37429     
37430     //{
37431     //  dragElement : elm
37432     //  resizingElement: el,
37433         // optional..
37434     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37435     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37436         // existingProxy ???
37437     //}
37438     
37439     this.el = Roo.get(cfg.dragElement, true);
37440     this.el.dom.unselectable = "on";
37441     /** @private */
37442     this.resizingEl = Roo.get(cfg.resizingElement, true);
37443
37444     /**
37445      * @private
37446      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37447      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37448      * @type Number
37449      */
37450     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37451     
37452     /**
37453      * The minimum size of the resizing element. (Defaults to 0)
37454      * @type Number
37455      */
37456     this.minSize = 0;
37457     
37458     /**
37459      * The maximum size of the resizing element. (Defaults to 2000)
37460      * @type Number
37461      */
37462     this.maxSize = 2000;
37463     
37464     /**
37465      * Whether to animate the transition to the new size
37466      * @type Boolean
37467      */
37468     this.animate = false;
37469     
37470     /**
37471      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37472      * @type Boolean
37473      */
37474     this.useShim = false;
37475     
37476     /** @private */
37477     this.shim = null;
37478     
37479     if(!cfg.existingProxy){
37480         /** @private */
37481         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37482     }else{
37483         this.proxy = Roo.get(cfg.existingProxy).dom;
37484     }
37485     /** @private */
37486     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37487     
37488     /** @private */
37489     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37490     
37491     /** @private */
37492     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37493     
37494     /** @private */
37495     this.dragSpecs = {};
37496     
37497     /**
37498      * @private The adapter to use to positon and resize elements
37499      */
37500     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37501     this.adapter.init(this);
37502     
37503     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37504         /** @private */
37505         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37506         this.el.addClass("roo-splitbar-h");
37507     }else{
37508         /** @private */
37509         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37510         this.el.addClass("roo-splitbar-v");
37511     }
37512     
37513     this.addEvents({
37514         /**
37515          * @event resize
37516          * Fires when the splitter is moved (alias for {@link #event-moved})
37517          * @param {Roo.bootstrap.SplitBar} this
37518          * @param {Number} newSize the new width or height
37519          */
37520         "resize" : true,
37521         /**
37522          * @event moved
37523          * Fires when the splitter is moved
37524          * @param {Roo.bootstrap.SplitBar} this
37525          * @param {Number} newSize the new width or height
37526          */
37527         "moved" : true,
37528         /**
37529          * @event beforeresize
37530          * Fires before the splitter is dragged
37531          * @param {Roo.bootstrap.SplitBar} this
37532          */
37533         "beforeresize" : true,
37534
37535         "beforeapply" : true
37536     });
37537
37538     Roo.util.Observable.call(this);
37539 };
37540
37541 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37542     onStartProxyDrag : function(x, y){
37543         this.fireEvent("beforeresize", this);
37544         if(!this.overlay){
37545             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37546             o.unselectable();
37547             o.enableDisplayMode("block");
37548             // all splitbars share the same overlay
37549             Roo.bootstrap.SplitBar.prototype.overlay = o;
37550         }
37551         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37552         this.overlay.show();
37553         Roo.get(this.proxy).setDisplayed("block");
37554         var size = this.adapter.getElementSize(this);
37555         this.activeMinSize = this.getMinimumSize();;
37556         this.activeMaxSize = this.getMaximumSize();;
37557         var c1 = size - this.activeMinSize;
37558         var c2 = Math.max(this.activeMaxSize - size, 0);
37559         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37560             this.dd.resetConstraints();
37561             this.dd.setXConstraint(
37562                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37563                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37564             );
37565             this.dd.setYConstraint(0, 0);
37566         }else{
37567             this.dd.resetConstraints();
37568             this.dd.setXConstraint(0, 0);
37569             this.dd.setYConstraint(
37570                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37571                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37572             );
37573          }
37574         this.dragSpecs.startSize = size;
37575         this.dragSpecs.startPoint = [x, y];
37576         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37577     },
37578     
37579     /** 
37580      * @private Called after the drag operation by the DDProxy
37581      */
37582     onEndProxyDrag : function(e){
37583         Roo.get(this.proxy).setDisplayed(false);
37584         var endPoint = Roo.lib.Event.getXY(e);
37585         if(this.overlay){
37586             this.overlay.hide();
37587         }
37588         var newSize;
37589         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37590             newSize = this.dragSpecs.startSize + 
37591                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37592                     endPoint[0] - this.dragSpecs.startPoint[0] :
37593                     this.dragSpecs.startPoint[0] - endPoint[0]
37594                 );
37595         }else{
37596             newSize = this.dragSpecs.startSize + 
37597                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37598                     endPoint[1] - this.dragSpecs.startPoint[1] :
37599                     this.dragSpecs.startPoint[1] - endPoint[1]
37600                 );
37601         }
37602         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37603         if(newSize != this.dragSpecs.startSize){
37604             if(this.fireEvent('beforeapply', this, newSize) !== false){
37605                 this.adapter.setElementSize(this, newSize);
37606                 this.fireEvent("moved", this, newSize);
37607                 this.fireEvent("resize", this, newSize);
37608             }
37609         }
37610     },
37611     
37612     /**
37613      * Get the adapter this SplitBar uses
37614      * @return The adapter object
37615      */
37616     getAdapter : function(){
37617         return this.adapter;
37618     },
37619     
37620     /**
37621      * Set the adapter this SplitBar uses
37622      * @param {Object} adapter A SplitBar adapter object
37623      */
37624     setAdapter : function(adapter){
37625         this.adapter = adapter;
37626         this.adapter.init(this);
37627     },
37628     
37629     /**
37630      * Gets the minimum size for the resizing element
37631      * @return {Number} The minimum size
37632      */
37633     getMinimumSize : function(){
37634         return this.minSize;
37635     },
37636     
37637     /**
37638      * Sets the minimum size for the resizing element
37639      * @param {Number} minSize The minimum size
37640      */
37641     setMinimumSize : function(minSize){
37642         this.minSize = minSize;
37643     },
37644     
37645     /**
37646      * Gets the maximum size for the resizing element
37647      * @return {Number} The maximum size
37648      */
37649     getMaximumSize : function(){
37650         return this.maxSize;
37651     },
37652     
37653     /**
37654      * Sets the maximum size for the resizing element
37655      * @param {Number} maxSize The maximum size
37656      */
37657     setMaximumSize : function(maxSize){
37658         this.maxSize = maxSize;
37659     },
37660     
37661     /**
37662      * Sets the initialize size for the resizing element
37663      * @param {Number} size The initial size
37664      */
37665     setCurrentSize : function(size){
37666         var oldAnimate = this.animate;
37667         this.animate = false;
37668         this.adapter.setElementSize(this, size);
37669         this.animate = oldAnimate;
37670     },
37671     
37672     /**
37673      * Destroy this splitbar. 
37674      * @param {Boolean} removeEl True to remove the element
37675      */
37676     destroy : function(removeEl){
37677         if(this.shim){
37678             this.shim.remove();
37679         }
37680         this.dd.unreg();
37681         this.proxy.parentNode.removeChild(this.proxy);
37682         if(removeEl){
37683             this.el.remove();
37684         }
37685     }
37686 });
37687
37688 /**
37689  * @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.
37690  */
37691 Roo.bootstrap.SplitBar.createProxy = function(dir){
37692     var proxy = new Roo.Element(document.createElement("div"));
37693     proxy.unselectable();
37694     var cls = 'roo-splitbar-proxy';
37695     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37696     document.body.appendChild(proxy.dom);
37697     return proxy.dom;
37698 };
37699
37700 /** 
37701  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37702  * Default Adapter. It assumes the splitter and resizing element are not positioned
37703  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37704  */
37705 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37706 };
37707
37708 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37709     // do nothing for now
37710     init : function(s){
37711     
37712     },
37713     /**
37714      * Called before drag operations to get the current size of the resizing element. 
37715      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37716      */
37717      getElementSize : function(s){
37718         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37719             return s.resizingEl.getWidth();
37720         }else{
37721             return s.resizingEl.getHeight();
37722         }
37723     },
37724     
37725     /**
37726      * Called after drag operations to set the size of the resizing element.
37727      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37728      * @param {Number} newSize The new size to set
37729      * @param {Function} onComplete A function to be invoked when resizing is complete
37730      */
37731     setElementSize : function(s, newSize, onComplete){
37732         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37733             if(!s.animate){
37734                 s.resizingEl.setWidth(newSize);
37735                 if(onComplete){
37736                     onComplete(s, newSize);
37737                 }
37738             }else{
37739                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37740             }
37741         }else{
37742             
37743             if(!s.animate){
37744                 s.resizingEl.setHeight(newSize);
37745                 if(onComplete){
37746                     onComplete(s, newSize);
37747                 }
37748             }else{
37749                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37750             }
37751         }
37752     }
37753 };
37754
37755 /** 
37756  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37757  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37758  * Adapter that  moves the splitter element to align with the resized sizing element. 
37759  * Used with an absolute positioned SplitBar.
37760  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37761  * document.body, make sure you assign an id to the body element.
37762  */
37763 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37764     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37765     this.container = Roo.get(container);
37766 };
37767
37768 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37769     init : function(s){
37770         this.basic.init(s);
37771     },
37772     
37773     getElementSize : function(s){
37774         return this.basic.getElementSize(s);
37775     },
37776     
37777     setElementSize : function(s, newSize, onComplete){
37778         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37779     },
37780     
37781     moveSplitter : function(s){
37782         var yes = Roo.bootstrap.SplitBar;
37783         switch(s.placement){
37784             case yes.LEFT:
37785                 s.el.setX(s.resizingEl.getRight());
37786                 break;
37787             case yes.RIGHT:
37788                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37789                 break;
37790             case yes.TOP:
37791                 s.el.setY(s.resizingEl.getBottom());
37792                 break;
37793             case yes.BOTTOM:
37794                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37795                 break;
37796         }
37797     }
37798 };
37799
37800 /**
37801  * Orientation constant - Create a vertical SplitBar
37802  * @static
37803  * @type Number
37804  */
37805 Roo.bootstrap.SplitBar.VERTICAL = 1;
37806
37807 /**
37808  * Orientation constant - Create a horizontal SplitBar
37809  * @static
37810  * @type Number
37811  */
37812 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37813
37814 /**
37815  * Placement constant - The resizing element is to the left of the splitter element
37816  * @static
37817  * @type Number
37818  */
37819 Roo.bootstrap.SplitBar.LEFT = 1;
37820
37821 /**
37822  * Placement constant - The resizing element is to the right of the splitter element
37823  * @static
37824  * @type Number
37825  */
37826 Roo.bootstrap.SplitBar.RIGHT = 2;
37827
37828 /**
37829  * Placement constant - The resizing element is positioned above the splitter element
37830  * @static
37831  * @type Number
37832  */
37833 Roo.bootstrap.SplitBar.TOP = 3;
37834
37835 /**
37836  * Placement constant - The resizing element is positioned under splitter element
37837  * @static
37838  * @type Number
37839  */
37840 Roo.bootstrap.SplitBar.BOTTOM = 4;
37841 /*
37842  * Based on:
37843  * Ext JS Library 1.1.1
37844  * Copyright(c) 2006-2007, Ext JS, LLC.
37845  *
37846  * Originally Released Under LGPL - original licence link has changed is not relivant.
37847  *
37848  * Fork - LGPL
37849  * <script type="text/javascript">
37850  */
37851
37852 /**
37853  * @class Roo.bootstrap.layout.Manager
37854  * @extends Roo.bootstrap.Component
37855  * @abstract
37856  * Base class for layout managers.
37857  */
37858 Roo.bootstrap.layout.Manager = function(config)
37859 {
37860     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37861
37862
37863
37864
37865
37866     /** false to disable window resize monitoring @type Boolean */
37867     this.monitorWindowResize = true;
37868     this.regions = {};
37869     this.addEvents({
37870         /**
37871          * @event layout
37872          * Fires when a layout is performed.
37873          * @param {Roo.LayoutManager} this
37874          */
37875         "layout" : true,
37876         /**
37877          * @event regionresized
37878          * Fires when the user resizes a region.
37879          * @param {Roo.LayoutRegion} region The resized region
37880          * @param {Number} newSize The new size (width for east/west, height for north/south)
37881          */
37882         "regionresized" : true,
37883         /**
37884          * @event regioncollapsed
37885          * Fires when a region is collapsed.
37886          * @param {Roo.LayoutRegion} region The collapsed region
37887          */
37888         "regioncollapsed" : true,
37889         /**
37890          * @event regionexpanded
37891          * Fires when a region is expanded.
37892          * @param {Roo.LayoutRegion} region The expanded region
37893          */
37894         "regionexpanded" : true
37895     });
37896     this.updating = false;
37897
37898     if (config.el) {
37899         this.el = Roo.get(config.el);
37900         this.initEvents();
37901     }
37902
37903 };
37904
37905 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37906
37907
37908     regions : null,
37909
37910     monitorWindowResize : true,
37911
37912
37913     updating : false,
37914
37915
37916     onRender : function(ct, position)
37917     {
37918         if(!this.el){
37919             this.el = Roo.get(ct);
37920             this.initEvents();
37921         }
37922         //this.fireEvent('render',this);
37923     },
37924
37925
37926     initEvents: function()
37927     {
37928
37929
37930         // ie scrollbar fix
37931         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37932             document.body.scroll = "no";
37933         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37934             this.el.position('relative');
37935         }
37936         this.id = this.el.id;
37937         this.el.addClass("roo-layout-container");
37938         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37939         if(this.el.dom != document.body ) {
37940             this.el.on('resize', this.layout,this);
37941             this.el.on('show', this.layout,this);
37942         }
37943
37944     },
37945
37946     /**
37947      * Returns true if this layout is currently being updated
37948      * @return {Boolean}
37949      */
37950     isUpdating : function(){
37951         return this.updating;
37952     },
37953
37954     /**
37955      * Suspend the LayoutManager from doing auto-layouts while
37956      * making multiple add or remove calls
37957      */
37958     beginUpdate : function(){
37959         this.updating = true;
37960     },
37961
37962     /**
37963      * Restore auto-layouts and optionally disable the manager from performing a layout
37964      * @param {Boolean} noLayout true to disable a layout update
37965      */
37966     endUpdate : function(noLayout){
37967         this.updating = false;
37968         if(!noLayout){
37969             this.layout();
37970         }
37971     },
37972
37973     layout: function(){
37974         // abstract...
37975     },
37976
37977     onRegionResized : function(region, newSize){
37978         this.fireEvent("regionresized", region, newSize);
37979         this.layout();
37980     },
37981
37982     onRegionCollapsed : function(region){
37983         this.fireEvent("regioncollapsed", region);
37984     },
37985
37986     onRegionExpanded : function(region){
37987         this.fireEvent("regionexpanded", region);
37988     },
37989
37990     /**
37991      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37992      * performs box-model adjustments.
37993      * @return {Object} The size as an object {width: (the width), height: (the height)}
37994      */
37995     getViewSize : function()
37996     {
37997         var size;
37998         if(this.el.dom != document.body){
37999             size = this.el.getSize();
38000         }else{
38001             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38002         }
38003         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38004         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38005         return size;
38006     },
38007
38008     /**
38009      * Returns the Element this layout is bound to.
38010      * @return {Roo.Element}
38011      */
38012     getEl : function(){
38013         return this.el;
38014     },
38015
38016     /**
38017      * Returns the specified region.
38018      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38019      * @return {Roo.LayoutRegion}
38020      */
38021     getRegion : function(target){
38022         return this.regions[target.toLowerCase()];
38023     },
38024
38025     onWindowResize : function(){
38026         if(this.monitorWindowResize){
38027             this.layout();
38028         }
38029     }
38030 });
38031 /*
38032  * Based on:
38033  * Ext JS Library 1.1.1
38034  * Copyright(c) 2006-2007, Ext JS, LLC.
38035  *
38036  * Originally Released Under LGPL - original licence link has changed is not relivant.
38037  *
38038  * Fork - LGPL
38039  * <script type="text/javascript">
38040  */
38041 /**
38042  * @class Roo.bootstrap.layout.Border
38043  * @extends Roo.bootstrap.layout.Manager
38044  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38045  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38046  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38047  * please see: examples/bootstrap/nested.html<br><br>
38048  
38049 <b>The container the layout is rendered into can be either the body element or any other element.
38050 If it is not the body element, the container needs to either be an absolute positioned element,
38051 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38052 the container size if it is not the body element.</b>
38053
38054 * @constructor
38055 * Create a new Border
38056 * @param {Object} config Configuration options
38057  */
38058 Roo.bootstrap.layout.Border = function(config){
38059     config = config || {};
38060     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38061     
38062     
38063     
38064     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38065         if(config[region]){
38066             config[region].region = region;
38067             this.addRegion(config[region]);
38068         }
38069     },this);
38070     
38071 };
38072
38073 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38074
38075 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38076     
38077         /**
38078          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38079          */
38080         /**
38081          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38082          */
38083         /**
38084          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38085          */
38086         /**
38087          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38088          */
38089         /**
38090          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38091          */
38092         
38093         
38094         
38095         
38096     parent : false, // this might point to a 'nest' or a ???
38097     
38098     /**
38099      * Creates and adds a new region if it doesn't already exist.
38100      * @param {String} target The target region key (north, south, east, west or center).
38101      * @param {Object} config The regions config object
38102      * @return {BorderLayoutRegion} The new region
38103      */
38104     addRegion : function(config)
38105     {
38106         if(!this.regions[config.region]){
38107             var r = this.factory(config);
38108             this.bindRegion(r);
38109         }
38110         return this.regions[config.region];
38111     },
38112
38113     // private (kinda)
38114     bindRegion : function(r){
38115         this.regions[r.config.region] = r;
38116         
38117         r.on("visibilitychange",    this.layout, this);
38118         r.on("paneladded",          this.layout, this);
38119         r.on("panelremoved",        this.layout, this);
38120         r.on("invalidated",         this.layout, this);
38121         r.on("resized",             this.onRegionResized, this);
38122         r.on("collapsed",           this.onRegionCollapsed, this);
38123         r.on("expanded",            this.onRegionExpanded, this);
38124     },
38125
38126     /**
38127      * Performs a layout update.
38128      */
38129     layout : function()
38130     {
38131         if(this.updating) {
38132             return;
38133         }
38134         
38135         // render all the rebions if they have not been done alreayd?
38136         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38137             if(this.regions[region] && !this.regions[region].bodyEl){
38138                 this.regions[region].onRender(this.el)
38139             }
38140         },this);
38141         
38142         var size = this.getViewSize();
38143         var w = size.width;
38144         var h = size.height;
38145         var centerW = w;
38146         var centerH = h;
38147         var centerY = 0;
38148         var centerX = 0;
38149         //var x = 0, y = 0;
38150
38151         var rs = this.regions;
38152         var north = rs["north"];
38153         var south = rs["south"]; 
38154         var west = rs["west"];
38155         var east = rs["east"];
38156         var center = rs["center"];
38157         //if(this.hideOnLayout){ // not supported anymore
38158             //c.el.setStyle("display", "none");
38159         //}
38160         if(north && north.isVisible()){
38161             var b = north.getBox();
38162             var m = north.getMargins();
38163             b.width = w - (m.left+m.right);
38164             b.x = m.left;
38165             b.y = m.top;
38166             centerY = b.height + b.y + m.bottom;
38167             centerH -= centerY;
38168             north.updateBox(this.safeBox(b));
38169         }
38170         if(south && south.isVisible()){
38171             var b = south.getBox();
38172             var m = south.getMargins();
38173             b.width = w - (m.left+m.right);
38174             b.x = m.left;
38175             var totalHeight = (b.height + m.top + m.bottom);
38176             b.y = h - totalHeight + m.top;
38177             centerH -= totalHeight;
38178             south.updateBox(this.safeBox(b));
38179         }
38180         if(west && west.isVisible()){
38181             var b = west.getBox();
38182             var m = west.getMargins();
38183             b.height = centerH - (m.top+m.bottom);
38184             b.x = m.left;
38185             b.y = centerY + m.top;
38186             var totalWidth = (b.width + m.left + m.right);
38187             centerX += totalWidth;
38188             centerW -= totalWidth;
38189             west.updateBox(this.safeBox(b));
38190         }
38191         if(east && east.isVisible()){
38192             var b = east.getBox();
38193             var m = east.getMargins();
38194             b.height = centerH - (m.top+m.bottom);
38195             var totalWidth = (b.width + m.left + m.right);
38196             b.x = w - totalWidth + m.left;
38197             b.y = centerY + m.top;
38198             centerW -= totalWidth;
38199             east.updateBox(this.safeBox(b));
38200         }
38201         if(center){
38202             var m = center.getMargins();
38203             var centerBox = {
38204                 x: centerX + m.left,
38205                 y: centerY + m.top,
38206                 width: centerW - (m.left+m.right),
38207                 height: centerH - (m.top+m.bottom)
38208             };
38209             //if(this.hideOnLayout){
38210                 //center.el.setStyle("display", "block");
38211             //}
38212             center.updateBox(this.safeBox(centerBox));
38213         }
38214         this.el.repaint();
38215         this.fireEvent("layout", this);
38216     },
38217
38218     // private
38219     safeBox : function(box){
38220         box.width = Math.max(0, box.width);
38221         box.height = Math.max(0, box.height);
38222         return box;
38223     },
38224
38225     /**
38226      * Adds a ContentPanel (or subclass) to this layout.
38227      * @param {String} target The target region key (north, south, east, west or center).
38228      * @param {Roo.ContentPanel} panel The panel to add
38229      * @return {Roo.ContentPanel} The added panel
38230      */
38231     add : function(target, panel){
38232          
38233         target = target.toLowerCase();
38234         return this.regions[target].add(panel);
38235     },
38236
38237     /**
38238      * Remove a ContentPanel (or subclass) to this layout.
38239      * @param {String} target The target region key (north, south, east, west or center).
38240      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38241      * @return {Roo.ContentPanel} The removed panel
38242      */
38243     remove : function(target, panel){
38244         target = target.toLowerCase();
38245         return this.regions[target].remove(panel);
38246     },
38247
38248     /**
38249      * Searches all regions for a panel with the specified id
38250      * @param {String} panelId
38251      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38252      */
38253     findPanel : function(panelId){
38254         var rs = this.regions;
38255         for(var target in rs){
38256             if(typeof rs[target] != "function"){
38257                 var p = rs[target].getPanel(panelId);
38258                 if(p){
38259                     return p;
38260                 }
38261             }
38262         }
38263         return null;
38264     },
38265
38266     /**
38267      * Searches all regions for a panel with the specified id and activates (shows) it.
38268      * @param {String/ContentPanel} panelId The panels id or the panel itself
38269      * @return {Roo.ContentPanel} The shown panel or null
38270      */
38271     showPanel : function(panelId) {
38272       var rs = this.regions;
38273       for(var target in rs){
38274          var r = rs[target];
38275          if(typeof r != "function"){
38276             if(r.hasPanel(panelId)){
38277                return r.showPanel(panelId);
38278             }
38279          }
38280       }
38281       return null;
38282    },
38283
38284    /**
38285      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38286      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38287      */
38288    /*
38289     restoreState : function(provider){
38290         if(!provider){
38291             provider = Roo.state.Manager;
38292         }
38293         var sm = new Roo.LayoutStateManager();
38294         sm.init(this, provider);
38295     },
38296 */
38297  
38298  
38299     /**
38300      * Adds a xtype elements to the layout.
38301      * <pre><code>
38302
38303 layout.addxtype({
38304        xtype : 'ContentPanel',
38305        region: 'west',
38306        items: [ .... ]
38307    }
38308 );
38309
38310 layout.addxtype({
38311         xtype : 'NestedLayoutPanel',
38312         region: 'west',
38313         layout: {
38314            center: { },
38315            west: { }   
38316         },
38317         items : [ ... list of content panels or nested layout panels.. ]
38318    }
38319 );
38320 </code></pre>
38321      * @param {Object} cfg Xtype definition of item to add.
38322      */
38323     addxtype : function(cfg)
38324     {
38325         // basically accepts a pannel...
38326         // can accept a layout region..!?!?
38327         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38328         
38329         
38330         // theory?  children can only be panels??
38331         
38332         //if (!cfg.xtype.match(/Panel$/)) {
38333         //    return false;
38334         //}
38335         var ret = false;
38336         
38337         if (typeof(cfg.region) == 'undefined') {
38338             Roo.log("Failed to add Panel, region was not set");
38339             Roo.log(cfg);
38340             return false;
38341         }
38342         var region = cfg.region;
38343         delete cfg.region;
38344         
38345           
38346         var xitems = [];
38347         if (cfg.items) {
38348             xitems = cfg.items;
38349             delete cfg.items;
38350         }
38351         var nb = false;
38352         
38353         if ( region == 'center') {
38354             Roo.log("Center: " + cfg.title);
38355         }
38356         
38357         
38358         switch(cfg.xtype) 
38359         {
38360             case 'Content':  // ContentPanel (el, cfg)
38361             case 'Scroll':  // ContentPanel (el, cfg)
38362             case 'View': 
38363                 cfg.autoCreate = cfg.autoCreate || true;
38364                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38365                 //} else {
38366                 //    var el = this.el.createChild();
38367                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38368                 //}
38369                 
38370                 this.add(region, ret);
38371                 break;
38372             
38373             /*
38374             case 'TreePanel': // our new panel!
38375                 cfg.el = this.el.createChild();
38376                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38377                 this.add(region, ret);
38378                 break;
38379             */
38380             
38381             case 'Nest': 
38382                 // create a new Layout (which is  a Border Layout...
38383                 
38384                 var clayout = cfg.layout;
38385                 clayout.el  = this.el.createChild();
38386                 clayout.items   = clayout.items  || [];
38387                 
38388                 delete cfg.layout;
38389                 
38390                 // replace this exitems with the clayout ones..
38391                 xitems = clayout.items;
38392                  
38393                 // force background off if it's in center...
38394                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38395                     cfg.background = false;
38396                 }
38397                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38398                 
38399                 
38400                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38401                 //console.log('adding nested layout panel '  + cfg.toSource());
38402                 this.add(region, ret);
38403                 nb = {}; /// find first...
38404                 break;
38405             
38406             case 'Grid':
38407                 
38408                 // needs grid and region
38409                 
38410                 //var el = this.getRegion(region).el.createChild();
38411                 /*
38412                  *var el = this.el.createChild();
38413                 // create the grid first...
38414                 cfg.grid.container = el;
38415                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38416                 */
38417                 
38418                 if (region == 'center' && this.active ) {
38419                     cfg.background = false;
38420                 }
38421                 
38422                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38423                 
38424                 this.add(region, ret);
38425                 /*
38426                 if (cfg.background) {
38427                     // render grid on panel activation (if panel background)
38428                     ret.on('activate', function(gp) {
38429                         if (!gp.grid.rendered) {
38430                     //        gp.grid.render(el);
38431                         }
38432                     });
38433                 } else {
38434                   //  cfg.grid.render(el);
38435                 }
38436                 */
38437                 break;
38438            
38439            
38440             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38441                 // it was the old xcomponent building that caused this before.
38442                 // espeically if border is the top element in the tree.
38443                 ret = this;
38444                 break; 
38445                 
38446                     
38447                 
38448                 
38449                 
38450             default:
38451                 /*
38452                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38453                     
38454                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38455                     this.add(region, ret);
38456                 } else {
38457                 */
38458                     Roo.log(cfg);
38459                     throw "Can not add '" + cfg.xtype + "' to Border";
38460                     return null;
38461              
38462                                 
38463              
38464         }
38465         this.beginUpdate();
38466         // add children..
38467         var region = '';
38468         var abn = {};
38469         Roo.each(xitems, function(i)  {
38470             region = nb && i.region ? i.region : false;
38471             
38472             var add = ret.addxtype(i);
38473            
38474             if (region) {
38475                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38476                 if (!i.background) {
38477                     abn[region] = nb[region] ;
38478                 }
38479             }
38480             
38481         });
38482         this.endUpdate();
38483
38484         // make the last non-background panel active..
38485         //if (nb) { Roo.log(abn); }
38486         if (nb) {
38487             
38488             for(var r in abn) {
38489                 region = this.getRegion(r);
38490                 if (region) {
38491                     // tried using nb[r], but it does not work..
38492                      
38493                     region.showPanel(abn[r]);
38494                    
38495                 }
38496             }
38497         }
38498         return ret;
38499         
38500     },
38501     
38502     
38503 // private
38504     factory : function(cfg)
38505     {
38506         
38507         var validRegions = Roo.bootstrap.layout.Border.regions;
38508
38509         var target = cfg.region;
38510         cfg.mgr = this;
38511         
38512         var r = Roo.bootstrap.layout;
38513         Roo.log(target);
38514         switch(target){
38515             case "north":
38516                 return new r.North(cfg);
38517             case "south":
38518                 return new r.South(cfg);
38519             case "east":
38520                 return new r.East(cfg);
38521             case "west":
38522                 return new r.West(cfg);
38523             case "center":
38524                 return new r.Center(cfg);
38525         }
38526         throw 'Layout region "'+target+'" not supported.';
38527     }
38528     
38529     
38530 });
38531  /*
38532  * Based on:
38533  * Ext JS Library 1.1.1
38534  * Copyright(c) 2006-2007, Ext JS, LLC.
38535  *
38536  * Originally Released Under LGPL - original licence link has changed is not relivant.
38537  *
38538  * Fork - LGPL
38539  * <script type="text/javascript">
38540  */
38541  
38542 /**
38543  * @class Roo.bootstrap.layout.Basic
38544  * @extends Roo.util.Observable
38545  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38546  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38547  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38548  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38549  * @cfg {string}   region  the region that it inhabits..
38550  * @cfg {bool}   skipConfig skip config?
38551  * 
38552
38553  */
38554 Roo.bootstrap.layout.Basic = function(config){
38555     
38556     this.mgr = config.mgr;
38557     
38558     this.position = config.region;
38559     
38560     var skipConfig = config.skipConfig;
38561     
38562     this.events = {
38563         /**
38564          * @scope Roo.BasicLayoutRegion
38565          */
38566         
38567         /**
38568          * @event beforeremove
38569          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38570          * @param {Roo.LayoutRegion} this
38571          * @param {Roo.ContentPanel} panel The panel
38572          * @param {Object} e The cancel event object
38573          */
38574         "beforeremove" : true,
38575         /**
38576          * @event invalidated
38577          * Fires when the layout for this region is changed.
38578          * @param {Roo.LayoutRegion} this
38579          */
38580         "invalidated" : true,
38581         /**
38582          * @event visibilitychange
38583          * Fires when this region is shown or hidden 
38584          * @param {Roo.LayoutRegion} this
38585          * @param {Boolean} visibility true or false
38586          */
38587         "visibilitychange" : true,
38588         /**
38589          * @event paneladded
38590          * Fires when a panel is added. 
38591          * @param {Roo.LayoutRegion} this
38592          * @param {Roo.ContentPanel} panel The panel
38593          */
38594         "paneladded" : true,
38595         /**
38596          * @event panelremoved
38597          * Fires when a panel is removed. 
38598          * @param {Roo.LayoutRegion} this
38599          * @param {Roo.ContentPanel} panel The panel
38600          */
38601         "panelremoved" : true,
38602         /**
38603          * @event beforecollapse
38604          * Fires when this region before collapse.
38605          * @param {Roo.LayoutRegion} this
38606          */
38607         "beforecollapse" : true,
38608         /**
38609          * @event collapsed
38610          * Fires when this region is collapsed.
38611          * @param {Roo.LayoutRegion} this
38612          */
38613         "collapsed" : true,
38614         /**
38615          * @event expanded
38616          * Fires when this region is expanded.
38617          * @param {Roo.LayoutRegion} this
38618          */
38619         "expanded" : true,
38620         /**
38621          * @event slideshow
38622          * Fires when this region is slid into view.
38623          * @param {Roo.LayoutRegion} this
38624          */
38625         "slideshow" : true,
38626         /**
38627          * @event slidehide
38628          * Fires when this region slides out of view. 
38629          * @param {Roo.LayoutRegion} this
38630          */
38631         "slidehide" : true,
38632         /**
38633          * @event panelactivated
38634          * Fires when a panel is activated. 
38635          * @param {Roo.LayoutRegion} this
38636          * @param {Roo.ContentPanel} panel The activated panel
38637          */
38638         "panelactivated" : true,
38639         /**
38640          * @event resized
38641          * Fires when the user resizes this region. 
38642          * @param {Roo.LayoutRegion} this
38643          * @param {Number} newSize The new size (width for east/west, height for north/south)
38644          */
38645         "resized" : true
38646     };
38647     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38648     this.panels = new Roo.util.MixedCollection();
38649     this.panels.getKey = this.getPanelId.createDelegate(this);
38650     this.box = null;
38651     this.activePanel = null;
38652     // ensure listeners are added...
38653     
38654     if (config.listeners || config.events) {
38655         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38656             listeners : config.listeners || {},
38657             events : config.events || {}
38658         });
38659     }
38660     
38661     if(skipConfig !== true){
38662         this.applyConfig(config);
38663     }
38664 };
38665
38666 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38667 {
38668     getPanelId : function(p){
38669         return p.getId();
38670     },
38671     
38672     applyConfig : function(config){
38673         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38674         this.config = config;
38675         
38676     },
38677     
38678     /**
38679      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38680      * the width, for horizontal (north, south) the height.
38681      * @param {Number} newSize The new width or height
38682      */
38683     resizeTo : function(newSize){
38684         var el = this.el ? this.el :
38685                  (this.activePanel ? this.activePanel.getEl() : null);
38686         if(el){
38687             switch(this.position){
38688                 case "east":
38689                 case "west":
38690                     el.setWidth(newSize);
38691                     this.fireEvent("resized", this, newSize);
38692                 break;
38693                 case "north":
38694                 case "south":
38695                     el.setHeight(newSize);
38696                     this.fireEvent("resized", this, newSize);
38697                 break;                
38698             }
38699         }
38700     },
38701     
38702     getBox : function(){
38703         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38704     },
38705     
38706     getMargins : function(){
38707         return this.margins;
38708     },
38709     
38710     updateBox : function(box){
38711         this.box = box;
38712         var el = this.activePanel.getEl();
38713         el.dom.style.left = box.x + "px";
38714         el.dom.style.top = box.y + "px";
38715         this.activePanel.setSize(box.width, box.height);
38716     },
38717     
38718     /**
38719      * Returns the container element for this region.
38720      * @return {Roo.Element}
38721      */
38722     getEl : function(){
38723         return this.activePanel;
38724     },
38725     
38726     /**
38727      * Returns true if this region is currently visible.
38728      * @return {Boolean}
38729      */
38730     isVisible : function(){
38731         return this.activePanel ? true : false;
38732     },
38733     
38734     setActivePanel : function(panel){
38735         panel = this.getPanel(panel);
38736         if(this.activePanel && this.activePanel != panel){
38737             this.activePanel.setActiveState(false);
38738             this.activePanel.getEl().setLeftTop(-10000,-10000);
38739         }
38740         this.activePanel = panel;
38741         panel.setActiveState(true);
38742         if(this.box){
38743             panel.setSize(this.box.width, this.box.height);
38744         }
38745         this.fireEvent("panelactivated", this, panel);
38746         this.fireEvent("invalidated");
38747     },
38748     
38749     /**
38750      * Show the specified panel.
38751      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38752      * @return {Roo.ContentPanel} The shown panel or null
38753      */
38754     showPanel : function(panel){
38755         panel = this.getPanel(panel);
38756         if(panel){
38757             this.setActivePanel(panel);
38758         }
38759         return panel;
38760     },
38761     
38762     /**
38763      * Get the active panel for this region.
38764      * @return {Roo.ContentPanel} The active panel or null
38765      */
38766     getActivePanel : function(){
38767         return this.activePanel;
38768     },
38769     
38770     /**
38771      * Add the passed ContentPanel(s)
38772      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38773      * @return {Roo.ContentPanel} The panel added (if only one was added)
38774      */
38775     add : function(panel){
38776         if(arguments.length > 1){
38777             for(var i = 0, len = arguments.length; i < len; i++) {
38778                 this.add(arguments[i]);
38779             }
38780             return null;
38781         }
38782         if(this.hasPanel(panel)){
38783             this.showPanel(panel);
38784             return panel;
38785         }
38786         var el = panel.getEl();
38787         if(el.dom.parentNode != this.mgr.el.dom){
38788             this.mgr.el.dom.appendChild(el.dom);
38789         }
38790         if(panel.setRegion){
38791             panel.setRegion(this);
38792         }
38793         this.panels.add(panel);
38794         el.setStyle("position", "absolute");
38795         if(!panel.background){
38796             this.setActivePanel(panel);
38797             if(this.config.initialSize && this.panels.getCount()==1){
38798                 this.resizeTo(this.config.initialSize);
38799             }
38800         }
38801         this.fireEvent("paneladded", this, panel);
38802         return panel;
38803     },
38804     
38805     /**
38806      * Returns true if the panel is in this region.
38807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38808      * @return {Boolean}
38809      */
38810     hasPanel : function(panel){
38811         if(typeof panel == "object"){ // must be panel obj
38812             panel = panel.getId();
38813         }
38814         return this.getPanel(panel) ? true : false;
38815     },
38816     
38817     /**
38818      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38819      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38820      * @param {Boolean} preservePanel Overrides the config preservePanel option
38821      * @return {Roo.ContentPanel} The panel that was removed
38822      */
38823     remove : function(panel, preservePanel){
38824         panel = this.getPanel(panel);
38825         if(!panel){
38826             return null;
38827         }
38828         var e = {};
38829         this.fireEvent("beforeremove", this, panel, e);
38830         if(e.cancel === true){
38831             return null;
38832         }
38833         var panelId = panel.getId();
38834         this.panels.removeKey(panelId);
38835         return panel;
38836     },
38837     
38838     /**
38839      * Returns the panel specified or null if it's not in this region.
38840      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38841      * @return {Roo.ContentPanel}
38842      */
38843     getPanel : function(id){
38844         if(typeof id == "object"){ // must be panel obj
38845             return id;
38846         }
38847         return this.panels.get(id);
38848     },
38849     
38850     /**
38851      * Returns this regions position (north/south/east/west/center).
38852      * @return {String} 
38853      */
38854     getPosition: function(){
38855         return this.position;    
38856     }
38857 });/*
38858  * Based on:
38859  * Ext JS Library 1.1.1
38860  * Copyright(c) 2006-2007, Ext JS, LLC.
38861  *
38862  * Originally Released Under LGPL - original licence link has changed is not relivant.
38863  *
38864  * Fork - LGPL
38865  * <script type="text/javascript">
38866  */
38867  
38868 /**
38869  * @class Roo.bootstrap.layout.Region
38870  * @extends Roo.bootstrap.layout.Basic
38871  * This class represents a region in a layout manager.
38872  
38873  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38874  * @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})
38875  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38876  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38877  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38878  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38879  * @cfg {String}    title           The title for the region (overrides panel titles)
38880  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38881  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38882  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38883  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38884  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38885  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38886  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38887  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38888  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38889  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38890
38891  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38892  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38893  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38894  * @cfg {Number}    width           For East/West panels
38895  * @cfg {Number}    height          For North/South panels
38896  * @cfg {Boolean}   split           To show the splitter
38897  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38898  * 
38899  * @cfg {string}   cls             Extra CSS classes to add to region
38900  * 
38901  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38902  * @cfg {string}   region  the region that it inhabits..
38903  *
38904
38905  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38906  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38907
38908  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38909  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38910  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38911  */
38912 Roo.bootstrap.layout.Region = function(config)
38913 {
38914     this.applyConfig(config);
38915
38916     var mgr = config.mgr;
38917     var pos = config.region;
38918     config.skipConfig = true;
38919     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38920     
38921     if (mgr.el) {
38922         this.onRender(mgr.el);   
38923     }
38924      
38925     this.visible = true;
38926     this.collapsed = false;
38927     this.unrendered_panels = [];
38928 };
38929
38930 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38931
38932     position: '', // set by wrapper (eg. north/south etc..)
38933     unrendered_panels : null,  // unrendered panels.
38934     
38935     tabPosition : false,
38936     
38937     mgr: false, // points to 'Border'
38938     
38939     
38940     createBody : function(){
38941         /** This region's body element 
38942         * @type Roo.Element */
38943         this.bodyEl = this.el.createChild({
38944                 tag: "div",
38945                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38946         });
38947     },
38948
38949     onRender: function(ctr, pos)
38950     {
38951         var dh = Roo.DomHelper;
38952         /** This region's container element 
38953         * @type Roo.Element */
38954         this.el = dh.append(ctr.dom, {
38955                 tag: "div",
38956                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38957             }, true);
38958         /** This region's title element 
38959         * @type Roo.Element */
38960     
38961         this.titleEl = dh.append(this.el.dom,  {
38962                 tag: "div",
38963                 unselectable: "on",
38964                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38965                 children:[
38966                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38967                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38968                 ]
38969             }, true);
38970         
38971         this.titleEl.enableDisplayMode();
38972         /** This region's title text element 
38973         * @type HTMLElement */
38974         this.titleTextEl = this.titleEl.dom.firstChild;
38975         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38976         /*
38977         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38978         this.closeBtn.enableDisplayMode();
38979         this.closeBtn.on("click", this.closeClicked, this);
38980         this.closeBtn.hide();
38981     */
38982         this.createBody(this.config);
38983         if(this.config.hideWhenEmpty){
38984             this.hide();
38985             this.on("paneladded", this.validateVisibility, this);
38986             this.on("panelremoved", this.validateVisibility, this);
38987         }
38988         if(this.autoScroll){
38989             this.bodyEl.setStyle("overflow", "auto");
38990         }else{
38991             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38992         }
38993         //if(c.titlebar !== false){
38994             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38995                 this.titleEl.hide();
38996             }else{
38997                 this.titleEl.show();
38998                 if(this.config.title){
38999                     this.titleTextEl.innerHTML = this.config.title;
39000                 }
39001             }
39002         //}
39003         if(this.config.collapsed){
39004             this.collapse(true);
39005         }
39006         if(this.config.hidden){
39007             this.hide();
39008         }
39009         
39010         if (this.unrendered_panels && this.unrendered_panels.length) {
39011             for (var i =0;i< this.unrendered_panels.length; i++) {
39012                 this.add(this.unrendered_panels[i]);
39013             }
39014             this.unrendered_panels = null;
39015             
39016         }
39017         
39018     },
39019     
39020     applyConfig : function(c)
39021     {
39022         /*
39023          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39024             var dh = Roo.DomHelper;
39025             if(c.titlebar !== false){
39026                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39027                 this.collapseBtn.on("click", this.collapse, this);
39028                 this.collapseBtn.enableDisplayMode();
39029                 /*
39030                 if(c.showPin === true || this.showPin){
39031                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39032                     this.stickBtn.enableDisplayMode();
39033                     this.stickBtn.on("click", this.expand, this);
39034                     this.stickBtn.hide();
39035                 }
39036                 
39037             }
39038             */
39039             /** This region's collapsed element
39040             * @type Roo.Element */
39041             /*
39042              *
39043             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39044                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39045             ]}, true);
39046             
39047             if(c.floatable !== false){
39048                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39049                this.collapsedEl.on("click", this.collapseClick, this);
39050             }
39051
39052             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39053                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39054                    id: "message", unselectable: "on", style:{"float":"left"}});
39055                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39056              }
39057             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39058             this.expandBtn.on("click", this.expand, this);
39059             
39060         }
39061         
39062         if(this.collapseBtn){
39063             this.collapseBtn.setVisible(c.collapsible == true);
39064         }
39065         
39066         this.cmargins = c.cmargins || this.cmargins ||
39067                          (this.position == "west" || this.position == "east" ?
39068                              {top: 0, left: 2, right:2, bottom: 0} :
39069                              {top: 2, left: 0, right:0, bottom: 2});
39070         */
39071         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39072         
39073         
39074         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39075         
39076         this.autoScroll = c.autoScroll || false;
39077         
39078         
39079        
39080         
39081         this.duration = c.duration || .30;
39082         this.slideDuration = c.slideDuration || .45;
39083         this.config = c;
39084        
39085     },
39086     /**
39087      * Returns true if this region is currently visible.
39088      * @return {Boolean}
39089      */
39090     isVisible : function(){
39091         return this.visible;
39092     },
39093
39094     /**
39095      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39096      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39097      */
39098     //setCollapsedTitle : function(title){
39099     //    title = title || "&#160;";
39100      //   if(this.collapsedTitleTextEl){
39101       //      this.collapsedTitleTextEl.innerHTML = title;
39102        // }
39103     //},
39104
39105     getBox : function(){
39106         var b;
39107       //  if(!this.collapsed){
39108             b = this.el.getBox(false, true);
39109        // }else{
39110           //  b = this.collapsedEl.getBox(false, true);
39111         //}
39112         return b;
39113     },
39114
39115     getMargins : function(){
39116         return this.margins;
39117         //return this.collapsed ? this.cmargins : this.margins;
39118     },
39119 /*
39120     highlight : function(){
39121         this.el.addClass("x-layout-panel-dragover");
39122     },
39123
39124     unhighlight : function(){
39125         this.el.removeClass("x-layout-panel-dragover");
39126     },
39127 */
39128     updateBox : function(box)
39129     {
39130         if (!this.bodyEl) {
39131             return; // not rendered yet..
39132         }
39133         
39134         this.box = box;
39135         if(!this.collapsed){
39136             this.el.dom.style.left = box.x + "px";
39137             this.el.dom.style.top = box.y + "px";
39138             this.updateBody(box.width, box.height);
39139         }else{
39140             this.collapsedEl.dom.style.left = box.x + "px";
39141             this.collapsedEl.dom.style.top = box.y + "px";
39142             this.collapsedEl.setSize(box.width, box.height);
39143         }
39144         if(this.tabs){
39145             this.tabs.autoSizeTabs();
39146         }
39147     },
39148
39149     updateBody : function(w, h)
39150     {
39151         if(w !== null){
39152             this.el.setWidth(w);
39153             w -= this.el.getBorderWidth("rl");
39154             if(this.config.adjustments){
39155                 w += this.config.adjustments[0];
39156             }
39157         }
39158         if(h !== null && h > 0){
39159             this.el.setHeight(h);
39160             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39161             h -= this.el.getBorderWidth("tb");
39162             if(this.config.adjustments){
39163                 h += this.config.adjustments[1];
39164             }
39165             this.bodyEl.setHeight(h);
39166             if(this.tabs){
39167                 h = this.tabs.syncHeight(h);
39168             }
39169         }
39170         if(this.panelSize){
39171             w = w !== null ? w : this.panelSize.width;
39172             h = h !== null ? h : this.panelSize.height;
39173         }
39174         if(this.activePanel){
39175             var el = this.activePanel.getEl();
39176             w = w !== null ? w : el.getWidth();
39177             h = h !== null ? h : el.getHeight();
39178             this.panelSize = {width: w, height: h};
39179             this.activePanel.setSize(w, h);
39180         }
39181         if(Roo.isIE && this.tabs){
39182             this.tabs.el.repaint();
39183         }
39184     },
39185
39186     /**
39187      * Returns the container element for this region.
39188      * @return {Roo.Element}
39189      */
39190     getEl : function(){
39191         return this.el;
39192     },
39193
39194     /**
39195      * Hides this region.
39196      */
39197     hide : function(){
39198         //if(!this.collapsed){
39199             this.el.dom.style.left = "-2000px";
39200             this.el.hide();
39201         //}else{
39202          //   this.collapsedEl.dom.style.left = "-2000px";
39203          //   this.collapsedEl.hide();
39204        // }
39205         this.visible = false;
39206         this.fireEvent("visibilitychange", this, false);
39207     },
39208
39209     /**
39210      * Shows this region if it was previously hidden.
39211      */
39212     show : function(){
39213         //if(!this.collapsed){
39214             this.el.show();
39215         //}else{
39216         //    this.collapsedEl.show();
39217        // }
39218         this.visible = true;
39219         this.fireEvent("visibilitychange", this, true);
39220     },
39221 /*
39222     closeClicked : function(){
39223         if(this.activePanel){
39224             this.remove(this.activePanel);
39225         }
39226     },
39227
39228     collapseClick : function(e){
39229         if(this.isSlid){
39230            e.stopPropagation();
39231            this.slideIn();
39232         }else{
39233            e.stopPropagation();
39234            this.slideOut();
39235         }
39236     },
39237 */
39238     /**
39239      * Collapses this region.
39240      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39241      */
39242     /*
39243     collapse : function(skipAnim, skipCheck = false){
39244         if(this.collapsed) {
39245             return;
39246         }
39247         
39248         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39249             
39250             this.collapsed = true;
39251             if(this.split){
39252                 this.split.el.hide();
39253             }
39254             if(this.config.animate && skipAnim !== true){
39255                 this.fireEvent("invalidated", this);
39256                 this.animateCollapse();
39257             }else{
39258                 this.el.setLocation(-20000,-20000);
39259                 this.el.hide();
39260                 this.collapsedEl.show();
39261                 this.fireEvent("collapsed", this);
39262                 this.fireEvent("invalidated", this);
39263             }
39264         }
39265         
39266     },
39267 */
39268     animateCollapse : function(){
39269         // overridden
39270     },
39271
39272     /**
39273      * Expands this region if it was previously collapsed.
39274      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39275      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39276      */
39277     /*
39278     expand : function(e, skipAnim){
39279         if(e) {
39280             e.stopPropagation();
39281         }
39282         if(!this.collapsed || this.el.hasActiveFx()) {
39283             return;
39284         }
39285         if(this.isSlid){
39286             this.afterSlideIn();
39287             skipAnim = true;
39288         }
39289         this.collapsed = false;
39290         if(this.config.animate && skipAnim !== true){
39291             this.animateExpand();
39292         }else{
39293             this.el.show();
39294             if(this.split){
39295                 this.split.el.show();
39296             }
39297             this.collapsedEl.setLocation(-2000,-2000);
39298             this.collapsedEl.hide();
39299             this.fireEvent("invalidated", this);
39300             this.fireEvent("expanded", this);
39301         }
39302     },
39303 */
39304     animateExpand : function(){
39305         // overridden
39306     },
39307
39308     initTabs : function()
39309     {
39310         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39311         
39312         var ts = new Roo.bootstrap.panel.Tabs({
39313             el: this.bodyEl.dom,
39314             region : this,
39315             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39316             disableTooltips: this.config.disableTabTips,
39317             toolbar : this.config.toolbar
39318         });
39319         
39320         if(this.config.hideTabs){
39321             ts.stripWrap.setDisplayed(false);
39322         }
39323         this.tabs = ts;
39324         ts.resizeTabs = this.config.resizeTabs === true;
39325         ts.minTabWidth = this.config.minTabWidth || 40;
39326         ts.maxTabWidth = this.config.maxTabWidth || 250;
39327         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39328         ts.monitorResize = false;
39329         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39330         ts.bodyEl.addClass('roo-layout-tabs-body');
39331         this.panels.each(this.initPanelAsTab, this);
39332     },
39333
39334     initPanelAsTab : function(panel){
39335         var ti = this.tabs.addTab(
39336             panel.getEl().id,
39337             panel.getTitle(),
39338             null,
39339             this.config.closeOnTab && panel.isClosable(),
39340             panel.tpl
39341         );
39342         if(panel.tabTip !== undefined){
39343             ti.setTooltip(panel.tabTip);
39344         }
39345         ti.on("activate", function(){
39346               this.setActivePanel(panel);
39347         }, this);
39348         
39349         if(this.config.closeOnTab){
39350             ti.on("beforeclose", function(t, e){
39351                 e.cancel = true;
39352                 this.remove(panel);
39353             }, this);
39354         }
39355         
39356         panel.tabItem = ti;
39357         
39358         return ti;
39359     },
39360
39361     updatePanelTitle : function(panel, title)
39362     {
39363         if(this.activePanel == panel){
39364             this.updateTitle(title);
39365         }
39366         if(this.tabs){
39367             var ti = this.tabs.getTab(panel.getEl().id);
39368             ti.setText(title);
39369             if(panel.tabTip !== undefined){
39370                 ti.setTooltip(panel.tabTip);
39371             }
39372         }
39373     },
39374
39375     updateTitle : function(title){
39376         if(this.titleTextEl && !this.config.title){
39377             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39378         }
39379     },
39380
39381     setActivePanel : function(panel)
39382     {
39383         panel = this.getPanel(panel);
39384         if(this.activePanel && this.activePanel != panel){
39385             if(this.activePanel.setActiveState(false) === false){
39386                 return;
39387             }
39388         }
39389         this.activePanel = panel;
39390         panel.setActiveState(true);
39391         if(this.panelSize){
39392             panel.setSize(this.panelSize.width, this.panelSize.height);
39393         }
39394         if(this.closeBtn){
39395             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39396         }
39397         this.updateTitle(panel.getTitle());
39398         if(this.tabs){
39399             this.fireEvent("invalidated", this);
39400         }
39401         this.fireEvent("panelactivated", this, panel);
39402     },
39403
39404     /**
39405      * Shows the specified panel.
39406      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39407      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39408      */
39409     showPanel : function(panel)
39410     {
39411         panel = this.getPanel(panel);
39412         if(panel){
39413             if(this.tabs){
39414                 var tab = this.tabs.getTab(panel.getEl().id);
39415                 if(tab.isHidden()){
39416                     this.tabs.unhideTab(tab.id);
39417                 }
39418                 tab.activate();
39419             }else{
39420                 this.setActivePanel(panel);
39421             }
39422         }
39423         return panel;
39424     },
39425
39426     /**
39427      * Get the active panel for this region.
39428      * @return {Roo.ContentPanel} The active panel or null
39429      */
39430     getActivePanel : function(){
39431         return this.activePanel;
39432     },
39433
39434     validateVisibility : function(){
39435         if(this.panels.getCount() < 1){
39436             this.updateTitle("&#160;");
39437             this.closeBtn.hide();
39438             this.hide();
39439         }else{
39440             if(!this.isVisible()){
39441                 this.show();
39442             }
39443         }
39444     },
39445
39446     /**
39447      * Adds the passed ContentPanel(s) to this region.
39448      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39449      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39450      */
39451     add : function(panel)
39452     {
39453         if(arguments.length > 1){
39454             for(var i = 0, len = arguments.length; i < len; i++) {
39455                 this.add(arguments[i]);
39456             }
39457             return null;
39458         }
39459         
39460         // if we have not been rendered yet, then we can not really do much of this..
39461         if (!this.bodyEl) {
39462             this.unrendered_panels.push(panel);
39463             return panel;
39464         }
39465         
39466         
39467         
39468         
39469         if(this.hasPanel(panel)){
39470             this.showPanel(panel);
39471             return panel;
39472         }
39473         panel.setRegion(this);
39474         this.panels.add(panel);
39475        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39476             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39477             // and hide them... ???
39478             this.bodyEl.dom.appendChild(panel.getEl().dom);
39479             if(panel.background !== true){
39480                 this.setActivePanel(panel);
39481             }
39482             this.fireEvent("paneladded", this, panel);
39483             return panel;
39484         }
39485         */
39486         if(!this.tabs){
39487             this.initTabs();
39488         }else{
39489             this.initPanelAsTab(panel);
39490         }
39491         
39492         
39493         if(panel.background !== true){
39494             this.tabs.activate(panel.getEl().id);
39495         }
39496         this.fireEvent("paneladded", this, panel);
39497         return panel;
39498     },
39499
39500     /**
39501      * Hides the tab for the specified panel.
39502      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39503      */
39504     hidePanel : function(panel){
39505         if(this.tabs && (panel = this.getPanel(panel))){
39506             this.tabs.hideTab(panel.getEl().id);
39507         }
39508     },
39509
39510     /**
39511      * Unhides the tab for a previously hidden panel.
39512      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39513      */
39514     unhidePanel : function(panel){
39515         if(this.tabs && (panel = this.getPanel(panel))){
39516             this.tabs.unhideTab(panel.getEl().id);
39517         }
39518     },
39519
39520     clearPanels : function(){
39521         while(this.panels.getCount() > 0){
39522              this.remove(this.panels.first());
39523         }
39524     },
39525
39526     /**
39527      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39528      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39529      * @param {Boolean} preservePanel Overrides the config preservePanel option
39530      * @return {Roo.ContentPanel} The panel that was removed
39531      */
39532     remove : function(panel, preservePanel)
39533     {
39534         panel = this.getPanel(panel);
39535         if(!panel){
39536             return null;
39537         }
39538         var e = {};
39539         this.fireEvent("beforeremove", this, panel, e);
39540         if(e.cancel === true){
39541             return null;
39542         }
39543         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39544         var panelId = panel.getId();
39545         this.panels.removeKey(panelId);
39546         if(preservePanel){
39547             document.body.appendChild(panel.getEl().dom);
39548         }
39549         if(this.tabs){
39550             this.tabs.removeTab(panel.getEl().id);
39551         }else if (!preservePanel){
39552             this.bodyEl.dom.removeChild(panel.getEl().dom);
39553         }
39554         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39555             var p = this.panels.first();
39556             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39557             tempEl.appendChild(p.getEl().dom);
39558             this.bodyEl.update("");
39559             this.bodyEl.dom.appendChild(p.getEl().dom);
39560             tempEl = null;
39561             this.updateTitle(p.getTitle());
39562             this.tabs = null;
39563             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39564             this.setActivePanel(p);
39565         }
39566         panel.setRegion(null);
39567         if(this.activePanel == panel){
39568             this.activePanel = null;
39569         }
39570         if(this.config.autoDestroy !== false && preservePanel !== true){
39571             try{panel.destroy();}catch(e){}
39572         }
39573         this.fireEvent("panelremoved", this, panel);
39574         return panel;
39575     },
39576
39577     /**
39578      * Returns the TabPanel component used by this region
39579      * @return {Roo.TabPanel}
39580      */
39581     getTabs : function(){
39582         return this.tabs;
39583     },
39584
39585     createTool : function(parentEl, className){
39586         var btn = Roo.DomHelper.append(parentEl, {
39587             tag: "div",
39588             cls: "x-layout-tools-button",
39589             children: [ {
39590                 tag: "div",
39591                 cls: "roo-layout-tools-button-inner " + className,
39592                 html: "&#160;"
39593             }]
39594         }, true);
39595         btn.addClassOnOver("roo-layout-tools-button-over");
39596         return btn;
39597     }
39598 });/*
39599  * Based on:
39600  * Ext JS Library 1.1.1
39601  * Copyright(c) 2006-2007, Ext JS, LLC.
39602  *
39603  * Originally Released Under LGPL - original licence link has changed is not relivant.
39604  *
39605  * Fork - LGPL
39606  * <script type="text/javascript">
39607  */
39608  
39609
39610
39611 /**
39612  * @class Roo.SplitLayoutRegion
39613  * @extends Roo.LayoutRegion
39614  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39615  */
39616 Roo.bootstrap.layout.Split = function(config){
39617     this.cursor = config.cursor;
39618     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39619 };
39620
39621 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39622 {
39623     splitTip : "Drag to resize.",
39624     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39625     useSplitTips : false,
39626
39627     applyConfig : function(config){
39628         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39629     },
39630     
39631     onRender : function(ctr,pos) {
39632         
39633         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39634         if(!this.config.split){
39635             return;
39636         }
39637         if(!this.split){
39638             
39639             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39640                             tag: "div",
39641                             id: this.el.id + "-split",
39642                             cls: "roo-layout-split roo-layout-split-"+this.position,
39643                             html: "&#160;"
39644             });
39645             /** The SplitBar for this region 
39646             * @type Roo.SplitBar */
39647             // does not exist yet...
39648             Roo.log([this.position, this.orientation]);
39649             
39650             this.split = new Roo.bootstrap.SplitBar({
39651                 dragElement : splitEl,
39652                 resizingElement: this.el,
39653                 orientation : this.orientation
39654             });
39655             
39656             this.split.on("moved", this.onSplitMove, this);
39657             this.split.useShim = this.config.useShim === true;
39658             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39659             if(this.useSplitTips){
39660                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39661             }
39662             //if(config.collapsible){
39663             //    this.split.el.on("dblclick", this.collapse,  this);
39664             //}
39665         }
39666         if(typeof this.config.minSize != "undefined"){
39667             this.split.minSize = this.config.minSize;
39668         }
39669         if(typeof this.config.maxSize != "undefined"){
39670             this.split.maxSize = this.config.maxSize;
39671         }
39672         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39673             this.hideSplitter();
39674         }
39675         
39676     },
39677
39678     getHMaxSize : function(){
39679          var cmax = this.config.maxSize || 10000;
39680          var center = this.mgr.getRegion("center");
39681          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39682     },
39683
39684     getVMaxSize : function(){
39685          var cmax = this.config.maxSize || 10000;
39686          var center = this.mgr.getRegion("center");
39687          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39688     },
39689
39690     onSplitMove : function(split, newSize){
39691         this.fireEvent("resized", this, newSize);
39692     },
39693     
39694     /** 
39695      * Returns the {@link Roo.SplitBar} for this region.
39696      * @return {Roo.SplitBar}
39697      */
39698     getSplitBar : function(){
39699         return this.split;
39700     },
39701     
39702     hide : function(){
39703         this.hideSplitter();
39704         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39705     },
39706
39707     hideSplitter : function(){
39708         if(this.split){
39709             this.split.el.setLocation(-2000,-2000);
39710             this.split.el.hide();
39711         }
39712     },
39713
39714     show : function(){
39715         if(this.split){
39716             this.split.el.show();
39717         }
39718         Roo.bootstrap.layout.Split.superclass.show.call(this);
39719     },
39720     
39721     beforeSlide: function(){
39722         if(Roo.isGecko){// firefox overflow auto bug workaround
39723             this.bodyEl.clip();
39724             if(this.tabs) {
39725                 this.tabs.bodyEl.clip();
39726             }
39727             if(this.activePanel){
39728                 this.activePanel.getEl().clip();
39729                 
39730                 if(this.activePanel.beforeSlide){
39731                     this.activePanel.beforeSlide();
39732                 }
39733             }
39734         }
39735     },
39736     
39737     afterSlide : function(){
39738         if(Roo.isGecko){// firefox overflow auto bug workaround
39739             this.bodyEl.unclip();
39740             if(this.tabs) {
39741                 this.tabs.bodyEl.unclip();
39742             }
39743             if(this.activePanel){
39744                 this.activePanel.getEl().unclip();
39745                 if(this.activePanel.afterSlide){
39746                     this.activePanel.afterSlide();
39747                 }
39748             }
39749         }
39750     },
39751
39752     initAutoHide : function(){
39753         if(this.autoHide !== false){
39754             if(!this.autoHideHd){
39755                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39756                 this.autoHideHd = {
39757                     "mouseout": function(e){
39758                         if(!e.within(this.el, true)){
39759                             st.delay(500);
39760                         }
39761                     },
39762                     "mouseover" : function(e){
39763                         st.cancel();
39764                     },
39765                     scope : this
39766                 };
39767             }
39768             this.el.on(this.autoHideHd);
39769         }
39770     },
39771
39772     clearAutoHide : function(){
39773         if(this.autoHide !== false){
39774             this.el.un("mouseout", this.autoHideHd.mouseout);
39775             this.el.un("mouseover", this.autoHideHd.mouseover);
39776         }
39777     },
39778
39779     clearMonitor : function(){
39780         Roo.get(document).un("click", this.slideInIf, this);
39781     },
39782
39783     // these names are backwards but not changed for compat
39784     slideOut : function(){
39785         if(this.isSlid || this.el.hasActiveFx()){
39786             return;
39787         }
39788         this.isSlid = true;
39789         if(this.collapseBtn){
39790             this.collapseBtn.hide();
39791         }
39792         this.closeBtnState = this.closeBtn.getStyle('display');
39793         this.closeBtn.hide();
39794         if(this.stickBtn){
39795             this.stickBtn.show();
39796         }
39797         this.el.show();
39798         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39799         this.beforeSlide();
39800         this.el.setStyle("z-index", 10001);
39801         this.el.slideIn(this.getSlideAnchor(), {
39802             callback: function(){
39803                 this.afterSlide();
39804                 this.initAutoHide();
39805                 Roo.get(document).on("click", this.slideInIf, this);
39806                 this.fireEvent("slideshow", this);
39807             },
39808             scope: this,
39809             block: true
39810         });
39811     },
39812
39813     afterSlideIn : function(){
39814         this.clearAutoHide();
39815         this.isSlid = false;
39816         this.clearMonitor();
39817         this.el.setStyle("z-index", "");
39818         if(this.collapseBtn){
39819             this.collapseBtn.show();
39820         }
39821         this.closeBtn.setStyle('display', this.closeBtnState);
39822         if(this.stickBtn){
39823             this.stickBtn.hide();
39824         }
39825         this.fireEvent("slidehide", this);
39826     },
39827
39828     slideIn : function(cb){
39829         if(!this.isSlid || this.el.hasActiveFx()){
39830             Roo.callback(cb);
39831             return;
39832         }
39833         this.isSlid = false;
39834         this.beforeSlide();
39835         this.el.slideOut(this.getSlideAnchor(), {
39836             callback: function(){
39837                 this.el.setLeftTop(-10000, -10000);
39838                 this.afterSlide();
39839                 this.afterSlideIn();
39840                 Roo.callback(cb);
39841             },
39842             scope: this,
39843             block: true
39844         });
39845     },
39846     
39847     slideInIf : function(e){
39848         if(!e.within(this.el)){
39849             this.slideIn();
39850         }
39851     },
39852
39853     animateCollapse : function(){
39854         this.beforeSlide();
39855         this.el.setStyle("z-index", 20000);
39856         var anchor = this.getSlideAnchor();
39857         this.el.slideOut(anchor, {
39858             callback : function(){
39859                 this.el.setStyle("z-index", "");
39860                 this.collapsedEl.slideIn(anchor, {duration:.3});
39861                 this.afterSlide();
39862                 this.el.setLocation(-10000,-10000);
39863                 this.el.hide();
39864                 this.fireEvent("collapsed", this);
39865             },
39866             scope: this,
39867             block: true
39868         });
39869     },
39870
39871     animateExpand : function(){
39872         this.beforeSlide();
39873         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39874         this.el.setStyle("z-index", 20000);
39875         this.collapsedEl.hide({
39876             duration:.1
39877         });
39878         this.el.slideIn(this.getSlideAnchor(), {
39879             callback : function(){
39880                 this.el.setStyle("z-index", "");
39881                 this.afterSlide();
39882                 if(this.split){
39883                     this.split.el.show();
39884                 }
39885                 this.fireEvent("invalidated", this);
39886                 this.fireEvent("expanded", this);
39887             },
39888             scope: this,
39889             block: true
39890         });
39891     },
39892
39893     anchors : {
39894         "west" : "left",
39895         "east" : "right",
39896         "north" : "top",
39897         "south" : "bottom"
39898     },
39899
39900     sanchors : {
39901         "west" : "l",
39902         "east" : "r",
39903         "north" : "t",
39904         "south" : "b"
39905     },
39906
39907     canchors : {
39908         "west" : "tl-tr",
39909         "east" : "tr-tl",
39910         "north" : "tl-bl",
39911         "south" : "bl-tl"
39912     },
39913
39914     getAnchor : function(){
39915         return this.anchors[this.position];
39916     },
39917
39918     getCollapseAnchor : function(){
39919         return this.canchors[this.position];
39920     },
39921
39922     getSlideAnchor : function(){
39923         return this.sanchors[this.position];
39924     },
39925
39926     getAlignAdj : function(){
39927         var cm = this.cmargins;
39928         switch(this.position){
39929             case "west":
39930                 return [0, 0];
39931             break;
39932             case "east":
39933                 return [0, 0];
39934             break;
39935             case "north":
39936                 return [0, 0];
39937             break;
39938             case "south":
39939                 return [0, 0];
39940             break;
39941         }
39942     },
39943
39944     getExpandAdj : function(){
39945         var c = this.collapsedEl, cm = this.cmargins;
39946         switch(this.position){
39947             case "west":
39948                 return [-(cm.right+c.getWidth()+cm.left), 0];
39949             break;
39950             case "east":
39951                 return [cm.right+c.getWidth()+cm.left, 0];
39952             break;
39953             case "north":
39954                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39955             break;
39956             case "south":
39957                 return [0, cm.top+cm.bottom+c.getHeight()];
39958             break;
39959         }
39960     }
39961 });/*
39962  * Based on:
39963  * Ext JS Library 1.1.1
39964  * Copyright(c) 2006-2007, Ext JS, LLC.
39965  *
39966  * Originally Released Under LGPL - original licence link has changed is not relivant.
39967  *
39968  * Fork - LGPL
39969  * <script type="text/javascript">
39970  */
39971 /*
39972  * These classes are private internal classes
39973  */
39974 Roo.bootstrap.layout.Center = function(config){
39975     config.region = "center";
39976     Roo.bootstrap.layout.Region.call(this, config);
39977     this.visible = true;
39978     this.minWidth = config.minWidth || 20;
39979     this.minHeight = config.minHeight || 20;
39980 };
39981
39982 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39983     hide : function(){
39984         // center panel can't be hidden
39985     },
39986     
39987     show : function(){
39988         // center panel can't be hidden
39989     },
39990     
39991     getMinWidth: function(){
39992         return this.minWidth;
39993     },
39994     
39995     getMinHeight: function(){
39996         return this.minHeight;
39997     }
39998 });
39999
40000
40001
40002
40003  
40004
40005
40006
40007
40008
40009
40010 Roo.bootstrap.layout.North = function(config)
40011 {
40012     config.region = 'north';
40013     config.cursor = 'n-resize';
40014     
40015     Roo.bootstrap.layout.Split.call(this, config);
40016     
40017     
40018     if(this.split){
40019         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40020         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40021         this.split.el.addClass("roo-layout-split-v");
40022     }
40023     //var size = config.initialSize || config.height;
40024     //if(this.el && typeof size != "undefined"){
40025     //    this.el.setHeight(size);
40026     //}
40027 };
40028 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40029 {
40030     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40031      
40032      
40033     onRender : function(ctr, pos)
40034     {
40035         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40036         var size = this.config.initialSize || this.config.height;
40037         if(this.el && typeof size != "undefined"){
40038             this.el.setHeight(size);
40039         }
40040     
40041     },
40042     
40043     getBox : function(){
40044         if(this.collapsed){
40045             return this.collapsedEl.getBox();
40046         }
40047         var box = this.el.getBox();
40048         if(this.split){
40049             box.height += this.split.el.getHeight();
40050         }
40051         return box;
40052     },
40053     
40054     updateBox : function(box){
40055         if(this.split && !this.collapsed){
40056             box.height -= this.split.el.getHeight();
40057             this.split.el.setLeft(box.x);
40058             this.split.el.setTop(box.y+box.height);
40059             this.split.el.setWidth(box.width);
40060         }
40061         if(this.collapsed){
40062             this.updateBody(box.width, null);
40063         }
40064         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40065     }
40066 });
40067
40068
40069
40070
40071
40072 Roo.bootstrap.layout.South = function(config){
40073     config.region = 'south';
40074     config.cursor = 's-resize';
40075     Roo.bootstrap.layout.Split.call(this, config);
40076     if(this.split){
40077         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40078         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40079         this.split.el.addClass("roo-layout-split-v");
40080     }
40081     
40082 };
40083
40084 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40085     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40086     
40087     onRender : function(ctr, pos)
40088     {
40089         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40090         var size = this.config.initialSize || this.config.height;
40091         if(this.el && typeof size != "undefined"){
40092             this.el.setHeight(size);
40093         }
40094     
40095     },
40096     
40097     getBox : function(){
40098         if(this.collapsed){
40099             return this.collapsedEl.getBox();
40100         }
40101         var box = this.el.getBox();
40102         if(this.split){
40103             var sh = this.split.el.getHeight();
40104             box.height += sh;
40105             box.y -= sh;
40106         }
40107         return box;
40108     },
40109     
40110     updateBox : function(box){
40111         if(this.split && !this.collapsed){
40112             var sh = this.split.el.getHeight();
40113             box.height -= sh;
40114             box.y += sh;
40115             this.split.el.setLeft(box.x);
40116             this.split.el.setTop(box.y-sh);
40117             this.split.el.setWidth(box.width);
40118         }
40119         if(this.collapsed){
40120             this.updateBody(box.width, null);
40121         }
40122         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40123     }
40124 });
40125
40126 Roo.bootstrap.layout.East = function(config){
40127     config.region = "east";
40128     config.cursor = "e-resize";
40129     Roo.bootstrap.layout.Split.call(this, config);
40130     if(this.split){
40131         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40132         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40133         this.split.el.addClass("roo-layout-split-h");
40134     }
40135     
40136 };
40137 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40138     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40139     
40140     onRender : function(ctr, pos)
40141     {
40142         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40143         var size = this.config.initialSize || this.config.width;
40144         if(this.el && typeof size != "undefined"){
40145             this.el.setWidth(size);
40146         }
40147     
40148     },
40149     
40150     getBox : function(){
40151         if(this.collapsed){
40152             return this.collapsedEl.getBox();
40153         }
40154         var box = this.el.getBox();
40155         if(this.split){
40156             var sw = this.split.el.getWidth();
40157             box.width += sw;
40158             box.x -= sw;
40159         }
40160         return box;
40161     },
40162
40163     updateBox : function(box){
40164         if(this.split && !this.collapsed){
40165             var sw = this.split.el.getWidth();
40166             box.width -= sw;
40167             this.split.el.setLeft(box.x);
40168             this.split.el.setTop(box.y);
40169             this.split.el.setHeight(box.height);
40170             box.x += sw;
40171         }
40172         if(this.collapsed){
40173             this.updateBody(null, box.height);
40174         }
40175         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40176     }
40177 });
40178
40179 Roo.bootstrap.layout.West = function(config){
40180     config.region = "west";
40181     config.cursor = "w-resize";
40182     
40183     Roo.bootstrap.layout.Split.call(this, config);
40184     if(this.split){
40185         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40186         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40187         this.split.el.addClass("roo-layout-split-h");
40188     }
40189     
40190 };
40191 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40192     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40193     
40194     onRender: function(ctr, pos)
40195     {
40196         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40197         var size = this.config.initialSize || this.config.width;
40198         if(typeof size != "undefined"){
40199             this.el.setWidth(size);
40200         }
40201     },
40202     
40203     getBox : function(){
40204         if(this.collapsed){
40205             return this.collapsedEl.getBox();
40206         }
40207         var box = this.el.getBox();
40208         if (box.width == 0) {
40209             box.width = this.config.width; // kludge?
40210         }
40211         if(this.split){
40212             box.width += this.split.el.getWidth();
40213         }
40214         return box;
40215     },
40216     
40217     updateBox : function(box){
40218         if(this.split && !this.collapsed){
40219             var sw = this.split.el.getWidth();
40220             box.width -= sw;
40221             this.split.el.setLeft(box.x+box.width);
40222             this.split.el.setTop(box.y);
40223             this.split.el.setHeight(box.height);
40224         }
40225         if(this.collapsed){
40226             this.updateBody(null, box.height);
40227         }
40228         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40229     }
40230 });/*
40231  * Based on:
40232  * Ext JS Library 1.1.1
40233  * Copyright(c) 2006-2007, Ext JS, LLC.
40234  *
40235  * Originally Released Under LGPL - original licence link has changed is not relivant.
40236  *
40237  * Fork - LGPL
40238  * <script type="text/javascript">
40239  */
40240 /**
40241  * @class Roo.bootstrap.paenl.Content
40242  * @extends Roo.util.Observable
40243  * @children Roo.bootstrap.Component
40244  * @parent builder Roo.bootstrap.layout.Border
40245  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40246  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40247  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40248  * @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
40249  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40250  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40251  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40252  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40253  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40254  * @cfg {String} title          The title for this panel
40255  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40256  * @cfg {String} url            Calls {@link #setUrl} with this value
40257  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40258  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40259  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40260  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40261  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40262  * @cfg {Boolean} badges render the badges
40263  * @cfg {String} cls  extra classes to use  
40264  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40265  
40266  * @constructor
40267  * Create a new ContentPanel.
40268  * @param {String/Object} config A string to set only the title or a config object
40269  
40270  */
40271 Roo.bootstrap.panel.Content = function( config){
40272     
40273     this.tpl = config.tpl || false;
40274     
40275     var el = config.el;
40276     var content = config.content;
40277
40278     if(config.autoCreate){ // xtype is available if this is called from factory
40279         el = Roo.id();
40280     }
40281     this.el = Roo.get(el);
40282     if(!this.el && config && config.autoCreate){
40283         if(typeof config.autoCreate == "object"){
40284             if(!config.autoCreate.id){
40285                 config.autoCreate.id = config.id||el;
40286             }
40287             this.el = Roo.DomHelper.append(document.body,
40288                         config.autoCreate, true);
40289         }else{
40290             var elcfg =  {
40291                 tag: "div",
40292                 cls: (config.cls || '') +
40293                     (config.background ? ' bg-' + config.background : '') +
40294                     " roo-layout-inactive-content",
40295                 id: config.id||el
40296             };
40297             if (config.iframe) {
40298                 elcfg.cn = [
40299                     {
40300                         tag : 'iframe',
40301                         style : 'border: 0px',
40302                         src : 'about:blank'
40303                     }
40304                 ];
40305             }
40306               
40307             if (config.html) {
40308                 elcfg.html = config.html;
40309                 
40310             }
40311                         
40312             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40313             if (config.iframe) {
40314                 this.iframeEl = this.el.select('iframe',true).first();
40315             }
40316             
40317         }
40318     } 
40319     this.closable = false;
40320     this.loaded = false;
40321     this.active = false;
40322    
40323       
40324     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40325         
40326         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40327         
40328         this.wrapEl = this.el; //this.el.wrap();
40329         var ti = [];
40330         if (config.toolbar.items) {
40331             ti = config.toolbar.items ;
40332             delete config.toolbar.items ;
40333         }
40334         
40335         var nitems = [];
40336         this.toolbar.render(this.wrapEl, 'before');
40337         for(var i =0;i < ti.length;i++) {
40338           //  Roo.log(['add child', items[i]]);
40339             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40340         }
40341         this.toolbar.items = nitems;
40342         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40343         delete config.toolbar;
40344         
40345     }
40346     /*
40347     // xtype created footer. - not sure if will work as we normally have to render first..
40348     if (this.footer && !this.footer.el && this.footer.xtype) {
40349         if (!this.wrapEl) {
40350             this.wrapEl = this.el.wrap();
40351         }
40352     
40353         this.footer.container = this.wrapEl.createChild();
40354          
40355         this.footer = Roo.factory(this.footer, Roo);
40356         
40357     }
40358     */
40359     
40360      if(typeof config == "string"){
40361         this.title = config;
40362     }else{
40363         Roo.apply(this, config);
40364     }
40365     
40366     if(this.resizeEl){
40367         this.resizeEl = Roo.get(this.resizeEl, true);
40368     }else{
40369         this.resizeEl = this.el;
40370     }
40371     // handle view.xtype
40372     
40373  
40374     
40375     
40376     this.addEvents({
40377         /**
40378          * @event activate
40379          * Fires when this panel is activated. 
40380          * @param {Roo.ContentPanel} this
40381          */
40382         "activate" : true,
40383         /**
40384          * @event deactivate
40385          * Fires when this panel is activated. 
40386          * @param {Roo.ContentPanel} this
40387          */
40388         "deactivate" : true,
40389
40390         /**
40391          * @event resize
40392          * Fires when this panel is resized if fitToFrame is true.
40393          * @param {Roo.ContentPanel} this
40394          * @param {Number} width The width after any component adjustments
40395          * @param {Number} height The height after any component adjustments
40396          */
40397         "resize" : true,
40398         
40399          /**
40400          * @event render
40401          * Fires when this tab is created
40402          * @param {Roo.ContentPanel} this
40403          */
40404         "render" : true,
40405         
40406           /**
40407          * @event scroll
40408          * Fires when this content is scrolled
40409          * @param {Roo.ContentPanel} this
40410          * @param {Event} scrollEvent
40411          */
40412         "scroll" : true
40413         
40414         
40415         
40416     });
40417     
40418
40419     
40420     
40421     if(this.autoScroll && !this.iframe){
40422         this.resizeEl.setStyle("overflow", "auto");
40423         this.resizeEl.on('scroll', this.onScroll, this);
40424     } else {
40425         // fix randome scrolling
40426         //this.el.on('scroll', function() {
40427         //    Roo.log('fix random scolling');
40428         //    this.scrollTo('top',0); 
40429         //});
40430     }
40431     content = content || this.content;
40432     if(content){
40433         this.setContent(content);
40434     }
40435     if(config && config.url){
40436         this.setUrl(this.url, this.params, this.loadOnce);
40437     }
40438     
40439     
40440     
40441     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40442     
40443     if (this.view && typeof(this.view.xtype) != 'undefined') {
40444         this.view.el = this.el.appendChild(document.createElement("div"));
40445         this.view = Roo.factory(this.view); 
40446         this.view.render  &&  this.view.render(false, '');  
40447     }
40448     
40449     
40450     this.fireEvent('render', this);
40451 };
40452
40453 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40454     
40455     cls : '',
40456     background : '',
40457     
40458     tabTip : '',
40459     
40460     iframe : false,
40461     iframeEl : false,
40462     
40463     /* Resize Element - use this to work out scroll etc. */
40464     resizeEl : false,
40465     
40466     setRegion : function(region){
40467         this.region = region;
40468         this.setActiveClass(region && !this.background);
40469     },
40470     
40471     
40472     setActiveClass: function(state)
40473     {
40474         if(state){
40475            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40476            this.el.setStyle('position','relative');
40477         }else{
40478            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40479            this.el.setStyle('position', 'absolute');
40480         } 
40481     },
40482     
40483     /**
40484      * Returns the toolbar for this Panel if one was configured. 
40485      * @return {Roo.Toolbar} 
40486      */
40487     getToolbar : function(){
40488         return this.toolbar;
40489     },
40490     
40491     setActiveState : function(active)
40492     {
40493         this.active = active;
40494         this.setActiveClass(active);
40495         if(!active){
40496             if(this.fireEvent("deactivate", this) === false){
40497                 return false;
40498             }
40499             return true;
40500         }
40501         this.fireEvent("activate", this);
40502         return true;
40503     },
40504     /**
40505      * Updates this panel's element (not for iframe)
40506      * @param {String} content The new content
40507      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40508     */
40509     setContent : function(content, loadScripts){
40510         if (this.iframe) {
40511             return;
40512         }
40513         
40514         this.el.update(content, loadScripts);
40515     },
40516
40517     ignoreResize : function(w, h)
40518     {
40519         return false; // always resize?
40520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40521             return true;
40522         }else{
40523             this.lastSize = {width: w, height: h};
40524             return false;
40525         }
40526     },
40527     /**
40528      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40529      * @return {Roo.UpdateManager} The UpdateManager
40530      */
40531     getUpdateManager : function(){
40532         if (this.iframe) {
40533             return false;
40534         }
40535         return this.el.getUpdateManager();
40536     },
40537      /**
40538      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40539      * Does not work with IFRAME contents
40540      * @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:
40541 <pre><code>
40542 panel.load({
40543     url: "your-url.php",
40544     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40545     callback: yourFunction,
40546     scope: yourObject, //(optional scope)
40547     discardUrl: false,
40548     nocache: false,
40549     text: "Loading...",
40550     timeout: 30,
40551     scripts: false
40552 });
40553 </code></pre>
40554      
40555      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40556      * 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.
40557      * @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}
40558      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40559      * @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.
40560      * @return {Roo.ContentPanel} this
40561      */
40562     load : function(){
40563         
40564         if (this.iframe) {
40565             return this;
40566         }
40567         
40568         var um = this.el.getUpdateManager();
40569         um.update.apply(um, arguments);
40570         return this;
40571     },
40572
40573
40574     /**
40575      * 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.
40576      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40577      * @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)
40578      * @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)
40579      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40580      */
40581     setUrl : function(url, params, loadOnce){
40582         if (this.iframe) {
40583             this.iframeEl.dom.src = url;
40584             return false;
40585         }
40586         
40587         if(this.refreshDelegate){
40588             this.removeListener("activate", this.refreshDelegate);
40589         }
40590         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40591         this.on("activate", this.refreshDelegate);
40592         return this.el.getUpdateManager();
40593     },
40594     
40595     _handleRefresh : function(url, params, loadOnce){
40596         if(!loadOnce || !this.loaded){
40597             var updater = this.el.getUpdateManager();
40598             updater.update(url, params, this._setLoaded.createDelegate(this));
40599         }
40600     },
40601     
40602     _setLoaded : function(){
40603         this.loaded = true;
40604     }, 
40605     
40606     /**
40607      * Returns this panel's id
40608      * @return {String} 
40609      */
40610     getId : function(){
40611         return this.el.id;
40612     },
40613     
40614     /** 
40615      * Returns this panel's element - used by regiosn to add.
40616      * @return {Roo.Element} 
40617      */
40618     getEl : function(){
40619         return this.wrapEl || this.el;
40620     },
40621     
40622    
40623     
40624     adjustForComponents : function(width, height)
40625     {
40626         //Roo.log('adjustForComponents ');
40627         if(this.resizeEl != this.el){
40628             width -= this.el.getFrameWidth('lr');
40629             height -= this.el.getFrameWidth('tb');
40630         }
40631         if(this.toolbar){
40632             var te = this.toolbar.getEl();
40633             te.setWidth(width);
40634             height -= te.getHeight();
40635         }
40636         if(this.footer){
40637             var te = this.footer.getEl();
40638             te.setWidth(width);
40639             height -= te.getHeight();
40640         }
40641         
40642         
40643         if(this.adjustments){
40644             width += this.adjustments[0];
40645             height += this.adjustments[1];
40646         }
40647         return {"width": width, "height": height};
40648     },
40649     
40650     setSize : function(width, height){
40651         if(this.fitToFrame && !this.ignoreResize(width, height)){
40652             if(this.fitContainer && this.resizeEl != this.el){
40653                 this.el.setSize(width, height);
40654             }
40655             var size = this.adjustForComponents(width, height);
40656             if (this.iframe) {
40657                 this.iframeEl.setSize(width,height);
40658             }
40659             
40660             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40661             this.fireEvent('resize', this, size.width, size.height);
40662             
40663             
40664         }
40665     },
40666     
40667     /**
40668      * Returns this panel's title
40669      * @return {String} 
40670      */
40671     getTitle : function(){
40672         
40673         if (typeof(this.title) != 'object') {
40674             return this.title;
40675         }
40676         
40677         var t = '';
40678         for (var k in this.title) {
40679             if (!this.title.hasOwnProperty(k)) {
40680                 continue;
40681             }
40682             
40683             if (k.indexOf('-') >= 0) {
40684                 var s = k.split('-');
40685                 for (var i = 0; i<s.length; i++) {
40686                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40687                 }
40688             } else {
40689                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40690             }
40691         }
40692         return t;
40693     },
40694     
40695     /**
40696      * Set this panel's title
40697      * @param {String} title
40698      */
40699     setTitle : function(title){
40700         this.title = title;
40701         if(this.region){
40702             this.region.updatePanelTitle(this, title);
40703         }
40704     },
40705     
40706     /**
40707      * Returns true is this panel was configured to be closable
40708      * @return {Boolean} 
40709      */
40710     isClosable : function(){
40711         return this.closable;
40712     },
40713     
40714     beforeSlide : function(){
40715         this.el.clip();
40716         this.resizeEl.clip();
40717     },
40718     
40719     afterSlide : function(){
40720         this.el.unclip();
40721         this.resizeEl.unclip();
40722     },
40723     
40724     /**
40725      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40726      *   Will fail silently if the {@link #setUrl} method has not been called.
40727      *   This does not activate the panel, just updates its content.
40728      */
40729     refresh : function(){
40730         if(this.refreshDelegate){
40731            this.loaded = false;
40732            this.refreshDelegate();
40733         }
40734     },
40735     
40736     /**
40737      * Destroys this panel
40738      */
40739     destroy : function(){
40740         this.el.removeAllListeners();
40741         var tempEl = document.createElement("span");
40742         tempEl.appendChild(this.el.dom);
40743         tempEl.innerHTML = "";
40744         this.el.remove();
40745         this.el = null;
40746     },
40747     
40748     /**
40749      * form - if the content panel contains a form - this is a reference to it.
40750      * @type {Roo.form.Form}
40751      */
40752     form : false,
40753     /**
40754      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40755      *    This contains a reference to it.
40756      * @type {Roo.View}
40757      */
40758     view : false,
40759     
40760       /**
40761      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40762      * <pre><code>
40763
40764 layout.addxtype({
40765        xtype : 'Form',
40766        items: [ .... ]
40767    }
40768 );
40769
40770 </code></pre>
40771      * @param {Object} cfg Xtype definition of item to add.
40772      */
40773     
40774     
40775     getChildContainer: function () {
40776         return this.getEl();
40777     },
40778     
40779     
40780     onScroll : function(e)
40781     {
40782         this.fireEvent('scroll', this, e);
40783     }
40784     
40785     
40786     /*
40787         var  ret = new Roo.factory(cfg);
40788         return ret;
40789         
40790         
40791         // add form..
40792         if (cfg.xtype.match(/^Form$/)) {
40793             
40794             var el;
40795             //if (this.footer) {
40796             //    el = this.footer.container.insertSibling(false, 'before');
40797             //} else {
40798                 el = this.el.createChild();
40799             //}
40800
40801             this.form = new  Roo.form.Form(cfg);
40802             
40803             
40804             if ( this.form.allItems.length) {
40805                 this.form.render(el.dom);
40806             }
40807             return this.form;
40808         }
40809         // should only have one of theses..
40810         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40811             // views.. should not be just added - used named prop 'view''
40812             
40813             cfg.el = this.el.appendChild(document.createElement("div"));
40814             // factory?
40815             
40816             var ret = new Roo.factory(cfg);
40817              
40818              ret.render && ret.render(false, ''); // render blank..
40819             this.view = ret;
40820             return ret;
40821         }
40822         return false;
40823     }
40824     \*/
40825 });
40826  
40827 /**
40828  * @class Roo.bootstrap.panel.Grid
40829  * @extends Roo.bootstrap.panel.Content
40830  * @constructor
40831  * Create a new GridPanel.
40832  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40833  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40834  * @param {Object} config A the config object
40835   
40836  */
40837
40838
40839
40840 Roo.bootstrap.panel.Grid = function(config)
40841 {
40842     
40843       
40844     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40845         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40846
40847     config.el = this.wrapper;
40848     //this.el = this.wrapper;
40849     
40850       if (config.container) {
40851         // ctor'ed from a Border/panel.grid
40852         
40853         
40854         this.wrapper.setStyle("overflow", "hidden");
40855         this.wrapper.addClass('roo-grid-container');
40856
40857     }
40858     
40859     
40860     if(config.toolbar){
40861         var tool_el = this.wrapper.createChild();    
40862         this.toolbar = Roo.factory(config.toolbar);
40863         var ti = [];
40864         if (config.toolbar.items) {
40865             ti = config.toolbar.items ;
40866             delete config.toolbar.items ;
40867         }
40868         
40869         var nitems = [];
40870         this.toolbar.render(tool_el);
40871         for(var i =0;i < ti.length;i++) {
40872           //  Roo.log(['add child', items[i]]);
40873             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40874         }
40875         this.toolbar.items = nitems;
40876         
40877         delete config.toolbar;
40878     }
40879     
40880     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40881     config.grid.scrollBody = true;;
40882     config.grid.monitorWindowResize = false; // turn off autosizing
40883     config.grid.autoHeight = false;
40884     config.grid.autoWidth = false;
40885     
40886     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40887     
40888     if (config.background) {
40889         // render grid on panel activation (if panel background)
40890         this.on('activate', function(gp) {
40891             if (!gp.grid.rendered) {
40892                 gp.grid.render(this.wrapper);
40893                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40894             }
40895         });
40896             
40897     } else {
40898         this.grid.render(this.wrapper);
40899         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40900
40901     }
40902     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40903     // ??? needed ??? config.el = this.wrapper;
40904     
40905     
40906     
40907   
40908     // xtype created footer. - not sure if will work as we normally have to render first..
40909     if (this.footer && !this.footer.el && this.footer.xtype) {
40910         
40911         var ctr = this.grid.getView().getFooterPanel(true);
40912         this.footer.dataSource = this.grid.dataSource;
40913         this.footer = Roo.factory(this.footer, Roo);
40914         this.footer.render(ctr);
40915         
40916     }
40917     
40918     
40919     
40920     
40921      
40922 };
40923
40924 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40925 {
40926     // private
40927     is_resizing : false,
40928     
40929     getId : function(){
40930         return this.grid.id;
40931     },
40932     
40933     /**
40934      * Returns the grid for this panel
40935      * @return {Roo.bootstrap.Table} 
40936      */
40937     getGrid : function(){
40938         return this.grid;    
40939     },
40940     
40941     setSize : function(width, height)
40942     {
40943         if (this.is_resizing) {
40944             return;
40945         
40946         }
40947         this.is_resizing = true;
40948         if(!this.ignoreResize(width, height)){
40949             var grid = this.grid;
40950             var size = this.adjustForComponents(width, height);
40951             // tfoot is not a footer?
40952           
40953             
40954             var gridel = grid.getGridEl();
40955             gridel.setSize(size.width, size.height);
40956             
40957             var tbd = grid.getGridEl().select('tbody', true).first();
40958             var thd = grid.getGridEl().select('thead',true).first();
40959             var tbf= grid.getGridEl().select('tfoot', true).first();
40960
40961             if (tbf) {
40962                 size.height -= tbf.getHeight();
40963             }
40964             if (thd) {
40965                 size.height -= thd.getHeight();
40966             }
40967             
40968             tbd.setSize(size.width, size.height );
40969             // this is for the account management tab -seems to work there.
40970             var thd = grid.getGridEl().select('thead',true).first();
40971             //if (tbd) {
40972             //    tbd.setSize(size.width, size.height - thd.getHeight());
40973             //}
40974              
40975             grid.autoSize();
40976         }
40977         this.is_resizing = false;
40978     },
40979      
40980     
40981     
40982     beforeSlide : function(){
40983         this.grid.getView().scroller.clip();
40984     },
40985     
40986     afterSlide : function(){
40987         this.grid.getView().scroller.unclip();
40988     },
40989     
40990     destroy : function(){
40991         this.grid.destroy();
40992         delete this.grid;
40993         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40994     }
40995 });
40996
40997 /**
40998  * @class Roo.bootstrap.panel.Nest
40999  * @extends Roo.bootstrap.panel.Content
41000  * @constructor
41001  * Create a new Panel, that can contain a layout.Border.
41002  * 
41003  * 
41004  * @param {String/Object} config A string to set only the title or a config object
41005  */
41006 Roo.bootstrap.panel.Nest = function(config)
41007 {
41008     // construct with only one argument..
41009     /* FIXME - implement nicer consturctors
41010     if (layout.layout) {
41011         config = layout;
41012         layout = config.layout;
41013         delete config.layout;
41014     }
41015     if (layout.xtype && !layout.getEl) {
41016         // then layout needs constructing..
41017         layout = Roo.factory(layout, Roo);
41018     }
41019     */
41020     
41021     config.el =  config.layout.getEl();
41022     
41023     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41024     
41025     config.layout.monitorWindowResize = false; // turn off autosizing
41026     this.layout = config.layout;
41027     this.layout.getEl().addClass("roo-layout-nested-layout");
41028     this.layout.parent = this;
41029     
41030     
41031     
41032     
41033 };
41034
41035 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41036     /**
41037     * @cfg {Roo.BorderLayout} layout The layout for this panel
41038     */
41039     layout : false,
41040
41041     setSize : function(width, height){
41042         if(!this.ignoreResize(width, height)){
41043             var size = this.adjustForComponents(width, height);
41044             var el = this.layout.getEl();
41045             if (size.height < 1) {
41046                 el.setWidth(size.width);   
41047             } else {
41048                 el.setSize(size.width, size.height);
41049             }
41050             var touch = el.dom.offsetWidth;
41051             this.layout.layout();
41052             // ie requires a double layout on the first pass
41053             if(Roo.isIE && !this.initialized){
41054                 this.initialized = true;
41055                 this.layout.layout();
41056             }
41057         }
41058     },
41059     
41060     // activate all subpanels if not currently active..
41061     
41062     setActiveState : function(active){
41063         this.active = active;
41064         this.setActiveClass(active);
41065         
41066         if(!active){
41067             this.fireEvent("deactivate", this);
41068             return;
41069         }
41070         
41071         this.fireEvent("activate", this);
41072         // not sure if this should happen before or after..
41073         if (!this.layout) {
41074             return; // should not happen..
41075         }
41076         var reg = false;
41077         for (var r in this.layout.regions) {
41078             reg = this.layout.getRegion(r);
41079             if (reg.getActivePanel()) {
41080                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41081                 reg.setActivePanel(reg.getActivePanel());
41082                 continue;
41083             }
41084             if (!reg.panels.length) {
41085                 continue;
41086             }
41087             reg.showPanel(reg.getPanel(0));
41088         }
41089         
41090         
41091         
41092         
41093     },
41094     
41095     /**
41096      * Returns the nested BorderLayout for this panel
41097      * @return {Roo.BorderLayout} 
41098      */
41099     getLayout : function(){
41100         return this.layout;
41101     },
41102     
41103      /**
41104      * Adds a xtype elements to the layout of the nested panel
41105      * <pre><code>
41106
41107 panel.addxtype({
41108        xtype : 'ContentPanel',
41109        region: 'west',
41110        items: [ .... ]
41111    }
41112 );
41113
41114 panel.addxtype({
41115         xtype : 'NestedLayoutPanel',
41116         region: 'west',
41117         layout: {
41118            center: { },
41119            west: { }   
41120         },
41121         items : [ ... list of content panels or nested layout panels.. ]
41122    }
41123 );
41124 </code></pre>
41125      * @param {Object} cfg Xtype definition of item to add.
41126      */
41127     addxtype : function(cfg) {
41128         return this.layout.addxtype(cfg);
41129     
41130     }
41131 });/*
41132  * Based on:
41133  * Ext JS Library 1.1.1
41134  * Copyright(c) 2006-2007, Ext JS, LLC.
41135  *
41136  * Originally Released Under LGPL - original licence link has changed is not relivant.
41137  *
41138  * Fork - LGPL
41139  * <script type="text/javascript">
41140  */
41141 /**
41142  * @class Roo.TabPanel
41143  * @extends Roo.util.Observable
41144  * A lightweight tab container.
41145  * <br><br>
41146  * Usage:
41147  * <pre><code>
41148 // basic tabs 1, built from existing content
41149 var tabs = new Roo.TabPanel("tabs1");
41150 tabs.addTab("script", "View Script");
41151 tabs.addTab("markup", "View Markup");
41152 tabs.activate("script");
41153
41154 // more advanced tabs, built from javascript
41155 var jtabs = new Roo.TabPanel("jtabs");
41156 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41157
41158 // set up the UpdateManager
41159 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41160 var updater = tab2.getUpdateManager();
41161 updater.setDefaultUrl("ajax1.htm");
41162 tab2.on('activate', updater.refresh, updater, true);
41163
41164 // Use setUrl for Ajax loading
41165 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41166 tab3.setUrl("ajax2.htm", null, true);
41167
41168 // Disabled tab
41169 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41170 tab4.disable();
41171
41172 jtabs.activate("jtabs-1");
41173  * </code></pre>
41174  * @constructor
41175  * Create a new TabPanel.
41176  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41177  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41178  */
41179 Roo.bootstrap.panel.Tabs = function(config){
41180     /**
41181     * The container element for this TabPanel.
41182     * @type Roo.Element
41183     */
41184     this.el = Roo.get(config.el);
41185     delete config.el;
41186     if(config){
41187         if(typeof config == "boolean"){
41188             this.tabPosition = config ? "bottom" : "top";
41189         }else{
41190             Roo.apply(this, config);
41191         }
41192     }
41193     
41194     if(this.tabPosition == "bottom"){
41195         // if tabs are at the bottom = create the body first.
41196         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41197         this.el.addClass("roo-tabs-bottom");
41198     }
41199     // next create the tabs holders
41200     
41201     if (this.tabPosition == "west"){
41202         
41203         var reg = this.region; // fake it..
41204         while (reg) {
41205             if (!reg.mgr.parent) {
41206                 break;
41207             }
41208             reg = reg.mgr.parent.region;
41209         }
41210         Roo.log("got nest?");
41211         Roo.log(reg);
41212         if (reg.mgr.getRegion('west')) {
41213             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41214             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41215             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41216             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41217             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41218         
41219             
41220         }
41221         
41222         
41223     } else {
41224      
41225         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41226         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41227         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41228         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41229     }
41230     
41231     
41232     if(Roo.isIE){
41233         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41234     }
41235     
41236     // finally - if tabs are at the top, then create the body last..
41237     if(this.tabPosition != "bottom"){
41238         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41239          * @type Roo.Element
41240          */
41241         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41242         this.el.addClass("roo-tabs-top");
41243     }
41244     this.items = [];
41245
41246     this.bodyEl.setStyle("position", "relative");
41247
41248     this.active = null;
41249     this.activateDelegate = this.activate.createDelegate(this);
41250
41251     this.addEvents({
41252         /**
41253          * @event tabchange
41254          * Fires when the active tab changes
41255          * @param {Roo.TabPanel} this
41256          * @param {Roo.TabPanelItem} activePanel The new active tab
41257          */
41258         "tabchange": true,
41259         /**
41260          * @event beforetabchange
41261          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41262          * @param {Roo.TabPanel} this
41263          * @param {Object} e Set cancel to true on this object to cancel the tab change
41264          * @param {Roo.TabPanelItem} tab The tab being changed to
41265          */
41266         "beforetabchange" : true
41267     });
41268
41269     Roo.EventManager.onWindowResize(this.onResize, this);
41270     this.cpad = this.el.getPadding("lr");
41271     this.hiddenCount = 0;
41272
41273
41274     // toolbar on the tabbar support...
41275     if (this.toolbar) {
41276         alert("no toolbar support yet");
41277         this.toolbar  = false;
41278         /*
41279         var tcfg = this.toolbar;
41280         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41281         this.toolbar = new Roo.Toolbar(tcfg);
41282         if (Roo.isSafari) {
41283             var tbl = tcfg.container.child('table', true);
41284             tbl.setAttribute('width', '100%');
41285         }
41286         */
41287         
41288     }
41289    
41290
41291
41292     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41293 };
41294
41295 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41296     /*
41297      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41298      */
41299     tabPosition : "top",
41300     /*
41301      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41302      */
41303     currentTabWidth : 0,
41304     /*
41305      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41306      */
41307     minTabWidth : 40,
41308     /*
41309      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41310      */
41311     maxTabWidth : 250,
41312     /*
41313      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41314      */
41315     preferredTabWidth : 175,
41316     /*
41317      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41318      */
41319     resizeTabs : false,
41320     /*
41321      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41322      */
41323     monitorResize : true,
41324     /*
41325      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41326      */
41327     toolbar : false,  // set by caller..
41328     
41329     region : false, /// set by caller
41330     
41331     disableTooltips : true, // not used yet...
41332
41333     /**
41334      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41335      * @param {String} id The id of the div to use <b>or create</b>
41336      * @param {String} text The text for the tab
41337      * @param {String} content (optional) Content to put in the TabPanelItem body
41338      * @param {Boolean} closable (optional) True to create a close icon on the tab
41339      * @return {Roo.TabPanelItem} The created TabPanelItem
41340      */
41341     addTab : function(id, text, content, closable, tpl)
41342     {
41343         var item = new Roo.bootstrap.panel.TabItem({
41344             panel: this,
41345             id : id,
41346             text : text,
41347             closable : closable,
41348             tpl : tpl
41349         });
41350         this.addTabItem(item);
41351         if(content){
41352             item.setContent(content);
41353         }
41354         return item;
41355     },
41356
41357     /**
41358      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41359      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41360      * @return {Roo.TabPanelItem}
41361      */
41362     getTab : function(id){
41363         return this.items[id];
41364     },
41365
41366     /**
41367      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41368      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41369      */
41370     hideTab : function(id){
41371         var t = this.items[id];
41372         if(!t.isHidden()){
41373            t.setHidden(true);
41374            this.hiddenCount++;
41375            this.autoSizeTabs();
41376         }
41377     },
41378
41379     /**
41380      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41381      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41382      */
41383     unhideTab : function(id){
41384         var t = this.items[id];
41385         if(t.isHidden()){
41386            t.setHidden(false);
41387            this.hiddenCount--;
41388            this.autoSizeTabs();
41389         }
41390     },
41391
41392     /**
41393      * Adds an existing {@link Roo.TabPanelItem}.
41394      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41395      */
41396     addTabItem : function(item)
41397     {
41398         this.items[item.id] = item;
41399         this.items.push(item);
41400         this.autoSizeTabs();
41401       //  if(this.resizeTabs){
41402     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41403   //         this.autoSizeTabs();
41404 //        }else{
41405 //            item.autoSize();
41406        // }
41407     },
41408
41409     /**
41410      * Removes a {@link Roo.TabPanelItem}.
41411      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41412      */
41413     removeTab : function(id){
41414         var items = this.items;
41415         var tab = items[id];
41416         if(!tab) { return; }
41417         var index = items.indexOf(tab);
41418         if(this.active == tab && items.length > 1){
41419             var newTab = this.getNextAvailable(index);
41420             if(newTab) {
41421                 newTab.activate();
41422             }
41423         }
41424         this.stripEl.dom.removeChild(tab.pnode.dom);
41425         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41426             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41427         }
41428         items.splice(index, 1);
41429         delete this.items[tab.id];
41430         tab.fireEvent("close", tab);
41431         tab.purgeListeners();
41432         this.autoSizeTabs();
41433     },
41434
41435     getNextAvailable : function(start){
41436         var items = this.items;
41437         var index = start;
41438         // look for a next tab that will slide over to
41439         // replace the one being removed
41440         while(index < items.length){
41441             var item = items[++index];
41442             if(item && !item.isHidden()){
41443                 return item;
41444             }
41445         }
41446         // if one isn't found select the previous tab (on the left)
41447         index = start;
41448         while(index >= 0){
41449             var item = items[--index];
41450             if(item && !item.isHidden()){
41451                 return item;
41452             }
41453         }
41454         return null;
41455     },
41456
41457     /**
41458      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41459      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41460      */
41461     disableTab : function(id){
41462         var tab = this.items[id];
41463         if(tab && this.active != tab){
41464             tab.disable();
41465         }
41466     },
41467
41468     /**
41469      * Enables a {@link Roo.TabPanelItem} that is disabled.
41470      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41471      */
41472     enableTab : function(id){
41473         var tab = this.items[id];
41474         tab.enable();
41475     },
41476
41477     /**
41478      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41479      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41480      * @return {Roo.TabPanelItem} The TabPanelItem.
41481      */
41482     activate : function(id)
41483     {
41484         //Roo.log('activite:'  + id);
41485         
41486         var tab = this.items[id];
41487         if(!tab){
41488             return null;
41489         }
41490         if(tab == this.active || tab.disabled){
41491             return tab;
41492         }
41493         var e = {};
41494         this.fireEvent("beforetabchange", this, e, tab);
41495         if(e.cancel !== true && !tab.disabled){
41496             if(this.active){
41497                 this.active.hide();
41498             }
41499             this.active = this.items[id];
41500             this.active.show();
41501             this.fireEvent("tabchange", this, this.active);
41502         }
41503         return tab;
41504     },
41505
41506     /**
41507      * Gets the active {@link Roo.TabPanelItem}.
41508      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41509      */
41510     getActiveTab : function(){
41511         return this.active;
41512     },
41513
41514     /**
41515      * Updates the tab body element to fit the height of the container element
41516      * for overflow scrolling
41517      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41518      */
41519     syncHeight : function(targetHeight){
41520         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41521         var bm = this.bodyEl.getMargins();
41522         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41523         this.bodyEl.setHeight(newHeight);
41524         return newHeight;
41525     },
41526
41527     onResize : function(){
41528         if(this.monitorResize){
41529             this.autoSizeTabs();
41530         }
41531     },
41532
41533     /**
41534      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41535      */
41536     beginUpdate : function(){
41537         this.updating = true;
41538     },
41539
41540     /**
41541      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41542      */
41543     endUpdate : function(){
41544         this.updating = false;
41545         this.autoSizeTabs();
41546     },
41547
41548     /**
41549      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41550      */
41551     autoSizeTabs : function()
41552     {
41553         var count = this.items.length;
41554         var vcount = count - this.hiddenCount;
41555         
41556         if (vcount < 2) {
41557             this.stripEl.hide();
41558         } else {
41559             this.stripEl.show();
41560         }
41561         
41562         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41563             return;
41564         }
41565         
41566         
41567         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41568         var availWidth = Math.floor(w / vcount);
41569         var b = this.stripBody;
41570         if(b.getWidth() > w){
41571             var tabs = this.items;
41572             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41573             if(availWidth < this.minTabWidth){
41574                 /*if(!this.sleft){    // incomplete scrolling code
41575                     this.createScrollButtons();
41576                 }
41577                 this.showScroll();
41578                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41579             }
41580         }else{
41581             if(this.currentTabWidth < this.preferredTabWidth){
41582                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41583             }
41584         }
41585     },
41586
41587     /**
41588      * Returns the number of tabs in this TabPanel.
41589      * @return {Number}
41590      */
41591      getCount : function(){
41592          return this.items.length;
41593      },
41594
41595     /**
41596      * Resizes all the tabs to the passed width
41597      * @param {Number} The new width
41598      */
41599     setTabWidth : function(width){
41600         this.currentTabWidth = width;
41601         for(var i = 0, len = this.items.length; i < len; i++) {
41602                 if(!this.items[i].isHidden()) {
41603                 this.items[i].setWidth(width);
41604             }
41605         }
41606     },
41607
41608     /**
41609      * Destroys this TabPanel
41610      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41611      */
41612     destroy : function(removeEl){
41613         Roo.EventManager.removeResizeListener(this.onResize, this);
41614         for(var i = 0, len = this.items.length; i < len; i++){
41615             this.items[i].purgeListeners();
41616         }
41617         if(removeEl === true){
41618             this.el.update("");
41619             this.el.remove();
41620         }
41621     },
41622     
41623     createStrip : function(container)
41624     {
41625         var strip = document.createElement("nav");
41626         strip.className = Roo.bootstrap.version == 4 ?
41627             "navbar-light bg-light" : 
41628             "navbar navbar-default"; //"x-tabs-wrap";
41629         container.appendChild(strip);
41630         return strip;
41631     },
41632     
41633     createStripList : function(strip)
41634     {
41635         // div wrapper for retard IE
41636         // returns the "tr" element.
41637         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41638         //'<div class="x-tabs-strip-wrap">'+
41639           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41640           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41641         return strip.firstChild; //.firstChild.firstChild.firstChild;
41642     },
41643     createBody : function(container)
41644     {
41645         var body = document.createElement("div");
41646         Roo.id(body, "tab-body");
41647         //Roo.fly(body).addClass("x-tabs-body");
41648         Roo.fly(body).addClass("tab-content");
41649         container.appendChild(body);
41650         return body;
41651     },
41652     createItemBody :function(bodyEl, id){
41653         var body = Roo.getDom(id);
41654         if(!body){
41655             body = document.createElement("div");
41656             body.id = id;
41657         }
41658         //Roo.fly(body).addClass("x-tabs-item-body");
41659         Roo.fly(body).addClass("tab-pane");
41660          bodyEl.insertBefore(body, bodyEl.firstChild);
41661         return body;
41662     },
41663     /** @private */
41664     createStripElements :  function(stripEl, text, closable, tpl)
41665     {
41666         var td = document.createElement("li"); // was td..
41667         td.className = 'nav-item';
41668         
41669         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41670         
41671         
41672         stripEl.appendChild(td);
41673         /*if(closable){
41674             td.className = "x-tabs-closable";
41675             if(!this.closeTpl){
41676                 this.closeTpl = new Roo.Template(
41677                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41678                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41679                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41680                 );
41681             }
41682             var el = this.closeTpl.overwrite(td, {"text": text});
41683             var close = el.getElementsByTagName("div")[0];
41684             var inner = el.getElementsByTagName("em")[0];
41685             return {"el": el, "close": close, "inner": inner};
41686         } else {
41687         */
41688         // not sure what this is..
41689 //            if(!this.tabTpl){
41690                 //this.tabTpl = new Roo.Template(
41691                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41692                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41693                 //);
41694 //                this.tabTpl = new Roo.Template(
41695 //                   '<a href="#">' +
41696 //                   '<span unselectable="on"' +
41697 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41698 //                            ' >{text}</span></a>'
41699 //                );
41700 //                
41701 //            }
41702
41703
41704             var template = tpl || this.tabTpl || false;
41705             
41706             if(!template){
41707                 template =  new Roo.Template(
41708                         Roo.bootstrap.version == 4 ? 
41709                             (
41710                                 '<a class="nav-link" href="#" unselectable="on"' +
41711                                      (this.disableTooltips ? '' : ' title="{text}"') +
41712                                      ' >{text}</a>'
41713                             ) : (
41714                                 '<a class="nav-link" href="#">' +
41715                                 '<span unselectable="on"' +
41716                                          (this.disableTooltips ? '' : ' title="{text}"') +
41717                                     ' >{text}</span></a>'
41718                             )
41719                 );
41720             }
41721             
41722             switch (typeof(template)) {
41723                 case 'object' :
41724                     break;
41725                 case 'string' :
41726                     template = new Roo.Template(template);
41727                     break;
41728                 default :
41729                     break;
41730             }
41731             
41732             var el = template.overwrite(td, {"text": text});
41733             
41734             var inner = el.getElementsByTagName("span")[0];
41735             
41736             return {"el": el, "inner": inner};
41737             
41738     }
41739         
41740     
41741 });
41742
41743 /**
41744  * @class Roo.TabPanelItem
41745  * @extends Roo.util.Observable
41746  * Represents an individual item (tab plus body) in a TabPanel.
41747  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41748  * @param {String} id The id of this TabPanelItem
41749  * @param {String} text The text for the tab of this TabPanelItem
41750  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41751  */
41752 Roo.bootstrap.panel.TabItem = function(config){
41753     /**
41754      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41755      * @type Roo.TabPanel
41756      */
41757     this.tabPanel = config.panel;
41758     /**
41759      * The id for this TabPanelItem
41760      * @type String
41761      */
41762     this.id = config.id;
41763     /** @private */
41764     this.disabled = false;
41765     /** @private */
41766     this.text = config.text;
41767     /** @private */
41768     this.loaded = false;
41769     this.closable = config.closable;
41770
41771     /**
41772      * The body element for this TabPanelItem.
41773      * @type Roo.Element
41774      */
41775     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41776     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41777     this.bodyEl.setStyle("display", "block");
41778     this.bodyEl.setStyle("zoom", "1");
41779     //this.hideAction();
41780
41781     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41782     /** @private */
41783     this.el = Roo.get(els.el);
41784     this.inner = Roo.get(els.inner, true);
41785      this.textEl = Roo.bootstrap.version == 4 ?
41786         this.el : Roo.get(this.el.dom.firstChild, true);
41787
41788     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41789     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41790
41791     
41792 //    this.el.on("mousedown", this.onTabMouseDown, this);
41793     this.el.on("click", this.onTabClick, this);
41794     /** @private */
41795     if(config.closable){
41796         var c = Roo.get(els.close, true);
41797         c.dom.title = this.closeText;
41798         c.addClassOnOver("close-over");
41799         c.on("click", this.closeClick, this);
41800      }
41801
41802     this.addEvents({
41803          /**
41804          * @event activate
41805          * Fires when this tab becomes the active tab.
41806          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41807          * @param {Roo.TabPanelItem} this
41808          */
41809         "activate": true,
41810         /**
41811          * @event beforeclose
41812          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41813          * @param {Roo.TabPanelItem} this
41814          * @param {Object} e Set cancel to true on this object to cancel the close.
41815          */
41816         "beforeclose": true,
41817         /**
41818          * @event close
41819          * Fires when this tab is closed.
41820          * @param {Roo.TabPanelItem} this
41821          */
41822          "close": true,
41823         /**
41824          * @event deactivate
41825          * Fires when this tab is no longer the active tab.
41826          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41827          * @param {Roo.TabPanelItem} this
41828          */
41829          "deactivate" : true
41830     });
41831     this.hidden = false;
41832
41833     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41834 };
41835
41836 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41837            {
41838     purgeListeners : function(){
41839        Roo.util.Observable.prototype.purgeListeners.call(this);
41840        this.el.removeAllListeners();
41841     },
41842     /**
41843      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41844      */
41845     show : function(){
41846         this.status_node.addClass("active");
41847         this.showAction();
41848         if(Roo.isOpera){
41849             this.tabPanel.stripWrap.repaint();
41850         }
41851         this.fireEvent("activate", this.tabPanel, this);
41852     },
41853
41854     /**
41855      * Returns true if this tab is the active tab.
41856      * @return {Boolean}
41857      */
41858     isActive : function(){
41859         return this.tabPanel.getActiveTab() == this;
41860     },
41861
41862     /**
41863      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41864      */
41865     hide : function(){
41866         this.status_node.removeClass("active");
41867         this.hideAction();
41868         this.fireEvent("deactivate", this.tabPanel, this);
41869     },
41870
41871     hideAction : function(){
41872         this.bodyEl.hide();
41873         this.bodyEl.setStyle("position", "absolute");
41874         this.bodyEl.setLeft("-20000px");
41875         this.bodyEl.setTop("-20000px");
41876     },
41877
41878     showAction : function(){
41879         this.bodyEl.setStyle("position", "relative");
41880         this.bodyEl.setTop("");
41881         this.bodyEl.setLeft("");
41882         this.bodyEl.show();
41883     },
41884
41885     /**
41886      * Set the tooltip for the tab.
41887      * @param {String} tooltip The tab's tooltip
41888      */
41889     setTooltip : function(text){
41890         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41891             this.textEl.dom.qtip = text;
41892             this.textEl.dom.removeAttribute('title');
41893         }else{
41894             this.textEl.dom.title = text;
41895         }
41896     },
41897
41898     onTabClick : function(e){
41899         e.preventDefault();
41900         this.tabPanel.activate(this.id);
41901     },
41902
41903     onTabMouseDown : function(e){
41904         e.preventDefault();
41905         this.tabPanel.activate(this.id);
41906     },
41907 /*
41908     getWidth : function(){
41909         return this.inner.getWidth();
41910     },
41911
41912     setWidth : function(width){
41913         var iwidth = width - this.linode.getPadding("lr");
41914         this.inner.setWidth(iwidth);
41915         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41916         this.linode.setWidth(width);
41917     },
41918 */
41919     /**
41920      * Show or hide the tab
41921      * @param {Boolean} hidden True to hide or false to show.
41922      */
41923     setHidden : function(hidden){
41924         this.hidden = hidden;
41925         this.linode.setStyle("display", hidden ? "none" : "");
41926     },
41927
41928     /**
41929      * Returns true if this tab is "hidden"
41930      * @return {Boolean}
41931      */
41932     isHidden : function(){
41933         return this.hidden;
41934     },
41935
41936     /**
41937      * Returns the text for this tab
41938      * @return {String}
41939      */
41940     getText : function(){
41941         return this.text;
41942     },
41943     /*
41944     autoSize : function(){
41945         //this.el.beginMeasure();
41946         this.textEl.setWidth(1);
41947         /*
41948          *  #2804 [new] Tabs in Roojs
41949          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41950          */
41951         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41952         //this.el.endMeasure();
41953     //},
41954
41955     /**
41956      * Sets the text for the tab (Note: this also sets the tooltip text)
41957      * @param {String} text The tab's text and tooltip
41958      */
41959     setText : function(text){
41960         this.text = text;
41961         this.textEl.update(text);
41962         this.setTooltip(text);
41963         //if(!this.tabPanel.resizeTabs){
41964         //    this.autoSize();
41965         //}
41966     },
41967     /**
41968      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41969      */
41970     activate : function(){
41971         this.tabPanel.activate(this.id);
41972     },
41973
41974     /**
41975      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41976      */
41977     disable : function(){
41978         if(this.tabPanel.active != this){
41979             this.disabled = true;
41980             this.status_node.addClass("disabled");
41981         }
41982     },
41983
41984     /**
41985      * Enables this TabPanelItem if it was previously disabled.
41986      */
41987     enable : function(){
41988         this.disabled = false;
41989         this.status_node.removeClass("disabled");
41990     },
41991
41992     /**
41993      * Sets the content for this TabPanelItem.
41994      * @param {String} content The content
41995      * @param {Boolean} loadScripts true to look for and load scripts
41996      */
41997     setContent : function(content, loadScripts){
41998         this.bodyEl.update(content, loadScripts);
41999     },
42000
42001     /**
42002      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42003      * @return {Roo.UpdateManager} The UpdateManager
42004      */
42005     getUpdateManager : function(){
42006         return this.bodyEl.getUpdateManager();
42007     },
42008
42009     /**
42010      * Set a URL to be used to load the content for this TabPanelItem.
42011      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42012      * @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)
42013      * @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)
42014      * @return {Roo.UpdateManager} The UpdateManager
42015      */
42016     setUrl : function(url, params, loadOnce){
42017         if(this.refreshDelegate){
42018             this.un('activate', this.refreshDelegate);
42019         }
42020         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42021         this.on("activate", this.refreshDelegate);
42022         return this.bodyEl.getUpdateManager();
42023     },
42024
42025     /** @private */
42026     _handleRefresh : function(url, params, loadOnce){
42027         if(!loadOnce || !this.loaded){
42028             var updater = this.bodyEl.getUpdateManager();
42029             updater.update(url, params, this._setLoaded.createDelegate(this));
42030         }
42031     },
42032
42033     /**
42034      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42035      *   Will fail silently if the setUrl method has not been called.
42036      *   This does not activate the panel, just updates its content.
42037      */
42038     refresh : function(){
42039         if(this.refreshDelegate){
42040            this.loaded = false;
42041            this.refreshDelegate();
42042         }
42043     },
42044
42045     /** @private */
42046     _setLoaded : function(){
42047         this.loaded = true;
42048     },
42049
42050     /** @private */
42051     closeClick : function(e){
42052         var o = {};
42053         e.stopEvent();
42054         this.fireEvent("beforeclose", this, o);
42055         if(o.cancel !== true){
42056             this.tabPanel.removeTab(this.id);
42057         }
42058     },
42059     /**
42060      * The text displayed in the tooltip for the close icon.
42061      * @type String
42062      */
42063     closeText : "Close this tab"
42064 });
42065 /**
42066 *    This script refer to:
42067 *    Title: International Telephone Input
42068 *    Author: Jack O'Connor
42069 *    Code version:  v12.1.12
42070 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42071 **/
42072
42073 Roo.bootstrap.form.PhoneInputData = function() {
42074     var d = [
42075       [
42076         "Afghanistan (‫افغانستان‬‎)",
42077         "af",
42078         "93"
42079       ],
42080       [
42081         "Albania (Shqipëri)",
42082         "al",
42083         "355"
42084       ],
42085       [
42086         "Algeria (‫الجزائر‬‎)",
42087         "dz",
42088         "213"
42089       ],
42090       [
42091         "American Samoa",
42092         "as",
42093         "1684"
42094       ],
42095       [
42096         "Andorra",
42097         "ad",
42098         "376"
42099       ],
42100       [
42101         "Angola",
42102         "ao",
42103         "244"
42104       ],
42105       [
42106         "Anguilla",
42107         "ai",
42108         "1264"
42109       ],
42110       [
42111         "Antigua and Barbuda",
42112         "ag",
42113         "1268"
42114       ],
42115       [
42116         "Argentina",
42117         "ar",
42118         "54"
42119       ],
42120       [
42121         "Armenia (Հայաստան)",
42122         "am",
42123         "374"
42124       ],
42125       [
42126         "Aruba",
42127         "aw",
42128         "297"
42129       ],
42130       [
42131         "Australia",
42132         "au",
42133         "61",
42134         0
42135       ],
42136       [
42137         "Austria (Österreich)",
42138         "at",
42139         "43"
42140       ],
42141       [
42142         "Azerbaijan (Azərbaycan)",
42143         "az",
42144         "994"
42145       ],
42146       [
42147         "Bahamas",
42148         "bs",
42149         "1242"
42150       ],
42151       [
42152         "Bahrain (‫البحرين‬‎)",
42153         "bh",
42154         "973"
42155       ],
42156       [
42157         "Bangladesh (বাংলাদেশ)",
42158         "bd",
42159         "880"
42160       ],
42161       [
42162         "Barbados",
42163         "bb",
42164         "1246"
42165       ],
42166       [
42167         "Belarus (Беларусь)",
42168         "by",
42169         "375"
42170       ],
42171       [
42172         "Belgium (België)",
42173         "be",
42174         "32"
42175       ],
42176       [
42177         "Belize",
42178         "bz",
42179         "501"
42180       ],
42181       [
42182         "Benin (Bénin)",
42183         "bj",
42184         "229"
42185       ],
42186       [
42187         "Bermuda",
42188         "bm",
42189         "1441"
42190       ],
42191       [
42192         "Bhutan (འབྲུག)",
42193         "bt",
42194         "975"
42195       ],
42196       [
42197         "Bolivia",
42198         "bo",
42199         "591"
42200       ],
42201       [
42202         "Bosnia and Herzegovina (Босна и Херцеговина)",
42203         "ba",
42204         "387"
42205       ],
42206       [
42207         "Botswana",
42208         "bw",
42209         "267"
42210       ],
42211       [
42212         "Brazil (Brasil)",
42213         "br",
42214         "55"
42215       ],
42216       [
42217         "British Indian Ocean Territory",
42218         "io",
42219         "246"
42220       ],
42221       [
42222         "British Virgin Islands",
42223         "vg",
42224         "1284"
42225       ],
42226       [
42227         "Brunei",
42228         "bn",
42229         "673"
42230       ],
42231       [
42232         "Bulgaria (България)",
42233         "bg",
42234         "359"
42235       ],
42236       [
42237         "Burkina Faso",
42238         "bf",
42239         "226"
42240       ],
42241       [
42242         "Burundi (Uburundi)",
42243         "bi",
42244         "257"
42245       ],
42246       [
42247         "Cambodia (កម្ពុជា)",
42248         "kh",
42249         "855"
42250       ],
42251       [
42252         "Cameroon (Cameroun)",
42253         "cm",
42254         "237"
42255       ],
42256       [
42257         "Canada",
42258         "ca",
42259         "1",
42260         1,
42261         ["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"]
42262       ],
42263       [
42264         "Cape Verde (Kabu Verdi)",
42265         "cv",
42266         "238"
42267       ],
42268       [
42269         "Caribbean Netherlands",
42270         "bq",
42271         "599",
42272         1
42273       ],
42274       [
42275         "Cayman Islands",
42276         "ky",
42277         "1345"
42278       ],
42279       [
42280         "Central African Republic (République centrafricaine)",
42281         "cf",
42282         "236"
42283       ],
42284       [
42285         "Chad (Tchad)",
42286         "td",
42287         "235"
42288       ],
42289       [
42290         "Chile",
42291         "cl",
42292         "56"
42293       ],
42294       [
42295         "China (中国)",
42296         "cn",
42297         "86"
42298       ],
42299       [
42300         "Christmas Island",
42301         "cx",
42302         "61",
42303         2
42304       ],
42305       [
42306         "Cocos (Keeling) Islands",
42307         "cc",
42308         "61",
42309         1
42310       ],
42311       [
42312         "Colombia",
42313         "co",
42314         "57"
42315       ],
42316       [
42317         "Comoros (‫جزر القمر‬‎)",
42318         "km",
42319         "269"
42320       ],
42321       [
42322         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42323         "cd",
42324         "243"
42325       ],
42326       [
42327         "Congo (Republic) (Congo-Brazzaville)",
42328         "cg",
42329         "242"
42330       ],
42331       [
42332         "Cook Islands",
42333         "ck",
42334         "682"
42335       ],
42336       [
42337         "Costa Rica",
42338         "cr",
42339         "506"
42340       ],
42341       [
42342         "Côte d’Ivoire",
42343         "ci",
42344         "225"
42345       ],
42346       [
42347         "Croatia (Hrvatska)",
42348         "hr",
42349         "385"
42350       ],
42351       [
42352         "Cuba",
42353         "cu",
42354         "53"
42355       ],
42356       [
42357         "Curaçao",
42358         "cw",
42359         "599",
42360         0
42361       ],
42362       [
42363         "Cyprus (Κύπρος)",
42364         "cy",
42365         "357"
42366       ],
42367       [
42368         "Czech Republic (Česká republika)",
42369         "cz",
42370         "420"
42371       ],
42372       [
42373         "Denmark (Danmark)",
42374         "dk",
42375         "45"
42376       ],
42377       [
42378         "Djibouti",
42379         "dj",
42380         "253"
42381       ],
42382       [
42383         "Dominica",
42384         "dm",
42385         "1767"
42386       ],
42387       [
42388         "Dominican Republic (República Dominicana)",
42389         "do",
42390         "1",
42391         2,
42392         ["809", "829", "849"]
42393       ],
42394       [
42395         "Ecuador",
42396         "ec",
42397         "593"
42398       ],
42399       [
42400         "Egypt (‫مصر‬‎)",
42401         "eg",
42402         "20"
42403       ],
42404       [
42405         "El Salvador",
42406         "sv",
42407         "503"
42408       ],
42409       [
42410         "Equatorial Guinea (Guinea Ecuatorial)",
42411         "gq",
42412         "240"
42413       ],
42414       [
42415         "Eritrea",
42416         "er",
42417         "291"
42418       ],
42419       [
42420         "Estonia (Eesti)",
42421         "ee",
42422         "372"
42423       ],
42424       [
42425         "Ethiopia",
42426         "et",
42427         "251"
42428       ],
42429       [
42430         "Falkland Islands (Islas Malvinas)",
42431         "fk",
42432         "500"
42433       ],
42434       [
42435         "Faroe Islands (Føroyar)",
42436         "fo",
42437         "298"
42438       ],
42439       [
42440         "Fiji",
42441         "fj",
42442         "679"
42443       ],
42444       [
42445         "Finland (Suomi)",
42446         "fi",
42447         "358",
42448         0
42449       ],
42450       [
42451         "France",
42452         "fr",
42453         "33"
42454       ],
42455       [
42456         "French Guiana (Guyane française)",
42457         "gf",
42458         "594"
42459       ],
42460       [
42461         "French Polynesia (Polynésie française)",
42462         "pf",
42463         "689"
42464       ],
42465       [
42466         "Gabon",
42467         "ga",
42468         "241"
42469       ],
42470       [
42471         "Gambia",
42472         "gm",
42473         "220"
42474       ],
42475       [
42476         "Georgia (საქართველო)",
42477         "ge",
42478         "995"
42479       ],
42480       [
42481         "Germany (Deutschland)",
42482         "de",
42483         "49"
42484       ],
42485       [
42486         "Ghana (Gaana)",
42487         "gh",
42488         "233"
42489       ],
42490       [
42491         "Gibraltar",
42492         "gi",
42493         "350"
42494       ],
42495       [
42496         "Greece (Ελλάδα)",
42497         "gr",
42498         "30"
42499       ],
42500       [
42501         "Greenland (Kalaallit Nunaat)",
42502         "gl",
42503         "299"
42504       ],
42505       [
42506         "Grenada",
42507         "gd",
42508         "1473"
42509       ],
42510       [
42511         "Guadeloupe",
42512         "gp",
42513         "590",
42514         0
42515       ],
42516       [
42517         "Guam",
42518         "gu",
42519         "1671"
42520       ],
42521       [
42522         "Guatemala",
42523         "gt",
42524         "502"
42525       ],
42526       [
42527         "Guernsey",
42528         "gg",
42529         "44",
42530         1
42531       ],
42532       [
42533         "Guinea (Guinée)",
42534         "gn",
42535         "224"
42536       ],
42537       [
42538         "Guinea-Bissau (Guiné Bissau)",
42539         "gw",
42540         "245"
42541       ],
42542       [
42543         "Guyana",
42544         "gy",
42545         "592"
42546       ],
42547       [
42548         "Haiti",
42549         "ht",
42550         "509"
42551       ],
42552       [
42553         "Honduras",
42554         "hn",
42555         "504"
42556       ],
42557       [
42558         "Hong Kong (香港)",
42559         "hk",
42560         "852"
42561       ],
42562       [
42563         "Hungary (Magyarország)",
42564         "hu",
42565         "36"
42566       ],
42567       [
42568         "Iceland (Ísland)",
42569         "is",
42570         "354"
42571       ],
42572       [
42573         "India (भारत)",
42574         "in",
42575         "91"
42576       ],
42577       [
42578         "Indonesia",
42579         "id",
42580         "62"
42581       ],
42582       [
42583         "Iran (‫ایران‬‎)",
42584         "ir",
42585         "98"
42586       ],
42587       [
42588         "Iraq (‫العراق‬‎)",
42589         "iq",
42590         "964"
42591       ],
42592       [
42593         "Ireland",
42594         "ie",
42595         "353"
42596       ],
42597       [
42598         "Isle of Man",
42599         "im",
42600         "44",
42601         2
42602       ],
42603       [
42604         "Israel (‫ישראל‬‎)",
42605         "il",
42606         "972"
42607       ],
42608       [
42609         "Italy (Italia)",
42610         "it",
42611         "39",
42612         0
42613       ],
42614       [
42615         "Jamaica",
42616         "jm",
42617         "1876"
42618       ],
42619       [
42620         "Japan (日本)",
42621         "jp",
42622         "81"
42623       ],
42624       [
42625         "Jersey",
42626         "je",
42627         "44",
42628         3
42629       ],
42630       [
42631         "Jordan (‫الأردن‬‎)",
42632         "jo",
42633         "962"
42634       ],
42635       [
42636         "Kazakhstan (Казахстан)",
42637         "kz",
42638         "7",
42639         1
42640       ],
42641       [
42642         "Kenya",
42643         "ke",
42644         "254"
42645       ],
42646       [
42647         "Kiribati",
42648         "ki",
42649         "686"
42650       ],
42651       [
42652         "Kosovo",
42653         "xk",
42654         "383"
42655       ],
42656       [
42657         "Kuwait (‫الكويت‬‎)",
42658         "kw",
42659         "965"
42660       ],
42661       [
42662         "Kyrgyzstan (Кыргызстан)",
42663         "kg",
42664         "996"
42665       ],
42666       [
42667         "Laos (ລາວ)",
42668         "la",
42669         "856"
42670       ],
42671       [
42672         "Latvia (Latvija)",
42673         "lv",
42674         "371"
42675       ],
42676       [
42677         "Lebanon (‫لبنان‬‎)",
42678         "lb",
42679         "961"
42680       ],
42681       [
42682         "Lesotho",
42683         "ls",
42684         "266"
42685       ],
42686       [
42687         "Liberia",
42688         "lr",
42689         "231"
42690       ],
42691       [
42692         "Libya (‫ليبيا‬‎)",
42693         "ly",
42694         "218"
42695       ],
42696       [
42697         "Liechtenstein",
42698         "li",
42699         "423"
42700       ],
42701       [
42702         "Lithuania (Lietuva)",
42703         "lt",
42704         "370"
42705       ],
42706       [
42707         "Luxembourg",
42708         "lu",
42709         "352"
42710       ],
42711       [
42712         "Macau (澳門)",
42713         "mo",
42714         "853"
42715       ],
42716       [
42717         "Macedonia (FYROM) (Македонија)",
42718         "mk",
42719         "389"
42720       ],
42721       [
42722         "Madagascar (Madagasikara)",
42723         "mg",
42724         "261"
42725       ],
42726       [
42727         "Malawi",
42728         "mw",
42729         "265"
42730       ],
42731       [
42732         "Malaysia",
42733         "my",
42734         "60"
42735       ],
42736       [
42737         "Maldives",
42738         "mv",
42739         "960"
42740       ],
42741       [
42742         "Mali",
42743         "ml",
42744         "223"
42745       ],
42746       [
42747         "Malta",
42748         "mt",
42749         "356"
42750       ],
42751       [
42752         "Marshall Islands",
42753         "mh",
42754         "692"
42755       ],
42756       [
42757         "Martinique",
42758         "mq",
42759         "596"
42760       ],
42761       [
42762         "Mauritania (‫موريتانيا‬‎)",
42763         "mr",
42764         "222"
42765       ],
42766       [
42767         "Mauritius (Moris)",
42768         "mu",
42769         "230"
42770       ],
42771       [
42772         "Mayotte",
42773         "yt",
42774         "262",
42775         1
42776       ],
42777       [
42778         "Mexico (México)",
42779         "mx",
42780         "52"
42781       ],
42782       [
42783         "Micronesia",
42784         "fm",
42785         "691"
42786       ],
42787       [
42788         "Moldova (Republica Moldova)",
42789         "md",
42790         "373"
42791       ],
42792       [
42793         "Monaco",
42794         "mc",
42795         "377"
42796       ],
42797       [
42798         "Mongolia (Монгол)",
42799         "mn",
42800         "976"
42801       ],
42802       [
42803         "Montenegro (Crna Gora)",
42804         "me",
42805         "382"
42806       ],
42807       [
42808         "Montserrat",
42809         "ms",
42810         "1664"
42811       ],
42812       [
42813         "Morocco (‫المغرب‬‎)",
42814         "ma",
42815         "212",
42816         0
42817       ],
42818       [
42819         "Mozambique (Moçambique)",
42820         "mz",
42821         "258"
42822       ],
42823       [
42824         "Myanmar (Burma) (မြန်မာ)",
42825         "mm",
42826         "95"
42827       ],
42828       [
42829         "Namibia (Namibië)",
42830         "na",
42831         "264"
42832       ],
42833       [
42834         "Nauru",
42835         "nr",
42836         "674"
42837       ],
42838       [
42839         "Nepal (नेपाल)",
42840         "np",
42841         "977"
42842       ],
42843       [
42844         "Netherlands (Nederland)",
42845         "nl",
42846         "31"
42847       ],
42848       [
42849         "New Caledonia (Nouvelle-Calédonie)",
42850         "nc",
42851         "687"
42852       ],
42853       [
42854         "New Zealand",
42855         "nz",
42856         "64"
42857       ],
42858       [
42859         "Nicaragua",
42860         "ni",
42861         "505"
42862       ],
42863       [
42864         "Niger (Nijar)",
42865         "ne",
42866         "227"
42867       ],
42868       [
42869         "Nigeria",
42870         "ng",
42871         "234"
42872       ],
42873       [
42874         "Niue",
42875         "nu",
42876         "683"
42877       ],
42878       [
42879         "Norfolk Island",
42880         "nf",
42881         "672"
42882       ],
42883       [
42884         "North Korea (조선 민주주의 인민 공화국)",
42885         "kp",
42886         "850"
42887       ],
42888       [
42889         "Northern Mariana Islands",
42890         "mp",
42891         "1670"
42892       ],
42893       [
42894         "Norway (Norge)",
42895         "no",
42896         "47",
42897         0
42898       ],
42899       [
42900         "Oman (‫عُمان‬‎)",
42901         "om",
42902         "968"
42903       ],
42904       [
42905         "Pakistan (‫پاکستان‬‎)",
42906         "pk",
42907         "92"
42908       ],
42909       [
42910         "Palau",
42911         "pw",
42912         "680"
42913       ],
42914       [
42915         "Palestine (‫فلسطين‬‎)",
42916         "ps",
42917         "970"
42918       ],
42919       [
42920         "Panama (Panamá)",
42921         "pa",
42922         "507"
42923       ],
42924       [
42925         "Papua New Guinea",
42926         "pg",
42927         "675"
42928       ],
42929       [
42930         "Paraguay",
42931         "py",
42932         "595"
42933       ],
42934       [
42935         "Peru (Perú)",
42936         "pe",
42937         "51"
42938       ],
42939       [
42940         "Philippines",
42941         "ph",
42942         "63"
42943       ],
42944       [
42945         "Poland (Polska)",
42946         "pl",
42947         "48"
42948       ],
42949       [
42950         "Portugal",
42951         "pt",
42952         "351"
42953       ],
42954       [
42955         "Puerto Rico",
42956         "pr",
42957         "1",
42958         3,
42959         ["787", "939"]
42960       ],
42961       [
42962         "Qatar (‫قطر‬‎)",
42963         "qa",
42964         "974"
42965       ],
42966       [
42967         "Réunion (La Réunion)",
42968         "re",
42969         "262",
42970         0
42971       ],
42972       [
42973         "Romania (România)",
42974         "ro",
42975         "40"
42976       ],
42977       [
42978         "Russia (Россия)",
42979         "ru",
42980         "7",
42981         0
42982       ],
42983       [
42984         "Rwanda",
42985         "rw",
42986         "250"
42987       ],
42988       [
42989         "Saint Barthélemy",
42990         "bl",
42991         "590",
42992         1
42993       ],
42994       [
42995         "Saint Helena",
42996         "sh",
42997         "290"
42998       ],
42999       [
43000         "Saint Kitts and Nevis",
43001         "kn",
43002         "1869"
43003       ],
43004       [
43005         "Saint Lucia",
43006         "lc",
43007         "1758"
43008       ],
43009       [
43010         "Saint Martin (Saint-Martin (partie française))",
43011         "mf",
43012         "590",
43013         2
43014       ],
43015       [
43016         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43017         "pm",
43018         "508"
43019       ],
43020       [
43021         "Saint Vincent and the Grenadines",
43022         "vc",
43023         "1784"
43024       ],
43025       [
43026         "Samoa",
43027         "ws",
43028         "685"
43029       ],
43030       [
43031         "San Marino",
43032         "sm",
43033         "378"
43034       ],
43035       [
43036         "São Tomé and Príncipe (São Tomé e Príncipe)",
43037         "st",
43038         "239"
43039       ],
43040       [
43041         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43042         "sa",
43043         "966"
43044       ],
43045       [
43046         "Senegal (Sénégal)",
43047         "sn",
43048         "221"
43049       ],
43050       [
43051         "Serbia (Србија)",
43052         "rs",
43053         "381"
43054       ],
43055       [
43056         "Seychelles",
43057         "sc",
43058         "248"
43059       ],
43060       [
43061         "Sierra Leone",
43062         "sl",
43063         "232"
43064       ],
43065       [
43066         "Singapore",
43067         "sg",
43068         "65"
43069       ],
43070       [
43071         "Sint Maarten",
43072         "sx",
43073         "1721"
43074       ],
43075       [
43076         "Slovakia (Slovensko)",
43077         "sk",
43078         "421"
43079       ],
43080       [
43081         "Slovenia (Slovenija)",
43082         "si",
43083         "386"
43084       ],
43085       [
43086         "Solomon Islands",
43087         "sb",
43088         "677"
43089       ],
43090       [
43091         "Somalia (Soomaaliya)",
43092         "so",
43093         "252"
43094       ],
43095       [
43096         "South Africa",
43097         "za",
43098         "27"
43099       ],
43100       [
43101         "South Korea (대한민국)",
43102         "kr",
43103         "82"
43104       ],
43105       [
43106         "South Sudan (‫جنوب السودان‬‎)",
43107         "ss",
43108         "211"
43109       ],
43110       [
43111         "Spain (España)",
43112         "es",
43113         "34"
43114       ],
43115       [
43116         "Sri Lanka (ශ්‍රී ලංකාව)",
43117         "lk",
43118         "94"
43119       ],
43120       [
43121         "Sudan (‫السودان‬‎)",
43122         "sd",
43123         "249"
43124       ],
43125       [
43126         "Suriname",
43127         "sr",
43128         "597"
43129       ],
43130       [
43131         "Svalbard and Jan Mayen",
43132         "sj",
43133         "47",
43134         1
43135       ],
43136       [
43137         "Swaziland",
43138         "sz",
43139         "268"
43140       ],
43141       [
43142         "Sweden (Sverige)",
43143         "se",
43144         "46"
43145       ],
43146       [
43147         "Switzerland (Schweiz)",
43148         "ch",
43149         "41"
43150       ],
43151       [
43152         "Syria (‫سوريا‬‎)",
43153         "sy",
43154         "963"
43155       ],
43156       [
43157         "Taiwan (台灣)",
43158         "tw",
43159         "886"
43160       ],
43161       [
43162         "Tajikistan",
43163         "tj",
43164         "992"
43165       ],
43166       [
43167         "Tanzania",
43168         "tz",
43169         "255"
43170       ],
43171       [
43172         "Thailand (ไทย)",
43173         "th",
43174         "66"
43175       ],
43176       [
43177         "Timor-Leste",
43178         "tl",
43179         "670"
43180       ],
43181       [
43182         "Togo",
43183         "tg",
43184         "228"
43185       ],
43186       [
43187         "Tokelau",
43188         "tk",
43189         "690"
43190       ],
43191       [
43192         "Tonga",
43193         "to",
43194         "676"
43195       ],
43196       [
43197         "Trinidad and Tobago",
43198         "tt",
43199         "1868"
43200       ],
43201       [
43202         "Tunisia (‫تونس‬‎)",
43203         "tn",
43204         "216"
43205       ],
43206       [
43207         "Turkey (Türkiye)",
43208         "tr",
43209         "90"
43210       ],
43211       [
43212         "Turkmenistan",
43213         "tm",
43214         "993"
43215       ],
43216       [
43217         "Turks and Caicos Islands",
43218         "tc",
43219         "1649"
43220       ],
43221       [
43222         "Tuvalu",
43223         "tv",
43224         "688"
43225       ],
43226       [
43227         "U.S. Virgin Islands",
43228         "vi",
43229         "1340"
43230       ],
43231       [
43232         "Uganda",
43233         "ug",
43234         "256"
43235       ],
43236       [
43237         "Ukraine (Україна)",
43238         "ua",
43239         "380"
43240       ],
43241       [
43242         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43243         "ae",
43244         "971"
43245       ],
43246       [
43247         "United Kingdom",
43248         "gb",
43249         "44",
43250         0
43251       ],
43252       [
43253         "United States",
43254         "us",
43255         "1",
43256         0
43257       ],
43258       [
43259         "Uruguay",
43260         "uy",
43261         "598"
43262       ],
43263       [
43264         "Uzbekistan (Oʻzbekiston)",
43265         "uz",
43266         "998"
43267       ],
43268       [
43269         "Vanuatu",
43270         "vu",
43271         "678"
43272       ],
43273       [
43274         "Vatican City (Città del Vaticano)",
43275         "va",
43276         "39",
43277         1
43278       ],
43279       [
43280         "Venezuela",
43281         "ve",
43282         "58"
43283       ],
43284       [
43285         "Vietnam (Việt Nam)",
43286         "vn",
43287         "84"
43288       ],
43289       [
43290         "Wallis and Futuna (Wallis-et-Futuna)",
43291         "wf",
43292         "681"
43293       ],
43294       [
43295         "Western Sahara (‫الصحراء الغربية‬‎)",
43296         "eh",
43297         "212",
43298         1
43299       ],
43300       [
43301         "Yemen (‫اليمن‬‎)",
43302         "ye",
43303         "967"
43304       ],
43305       [
43306         "Zambia",
43307         "zm",
43308         "260"
43309       ],
43310       [
43311         "Zimbabwe",
43312         "zw",
43313         "263"
43314       ],
43315       [
43316         "Åland Islands",
43317         "ax",
43318         "358",
43319         1
43320       ]
43321   ];
43322   
43323   return d;
43324 }/**
43325 *    This script refer to:
43326 *    Title: International Telephone Input
43327 *    Author: Jack O'Connor
43328 *    Code version:  v12.1.12
43329 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43330 **/
43331
43332 /**
43333  * @class Roo.bootstrap.form.PhoneInput
43334  * @extends Roo.bootstrap.form.TriggerField
43335  * An input with International dial-code selection
43336  
43337  * @cfg {String} defaultDialCode default '+852'
43338  * @cfg {Array} preferedCountries default []
43339   
43340  * @constructor
43341  * Create a new PhoneInput.
43342  * @param {Object} config Configuration options
43343  */
43344
43345 Roo.bootstrap.form.PhoneInput = function(config) {
43346     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43347 };
43348
43349 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43350         /**
43351         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43352         */
43353         listWidth: undefined,
43354         
43355         selectedClass: 'active',
43356         
43357         invalidClass : "has-warning",
43358         
43359         validClass: 'has-success',
43360         
43361         allowed: '0123456789',
43362         
43363         max_length: 15,
43364         
43365         /**
43366          * @cfg {String} defaultDialCode The default dial code when initializing the input
43367          */
43368         defaultDialCode: '+852',
43369         
43370         /**
43371          * @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
43372          */
43373         preferedCountries: false,
43374         
43375         getAutoCreate : function()
43376         {
43377             var data = Roo.bootstrap.form.PhoneInputData();
43378             var align = this.labelAlign || this.parentLabelAlign();
43379             var id = Roo.id();
43380             
43381             this.allCountries = [];
43382             this.dialCodeMapping = [];
43383             
43384             for (var i = 0; i < data.length; i++) {
43385               var c = data[i];
43386               this.allCountries[i] = {
43387                 name: c[0],
43388                 iso2: c[1],
43389                 dialCode: c[2],
43390                 priority: c[3] || 0,
43391                 areaCodes: c[4] || null
43392               };
43393               this.dialCodeMapping[c[2]] = {
43394                   name: c[0],
43395                   iso2: c[1],
43396                   priority: c[3] || 0,
43397                   areaCodes: c[4] || null
43398               };
43399             }
43400             
43401             var cfg = {
43402                 cls: 'form-group',
43403                 cn: []
43404             };
43405             
43406             var input =  {
43407                 tag: 'input',
43408                 id : id,
43409                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43410                 maxlength: this.max_length,
43411                 cls : 'form-control tel-input',
43412                 autocomplete: 'new-password'
43413             };
43414             
43415             var hiddenInput = {
43416                 tag: 'input',
43417                 type: 'hidden',
43418                 cls: 'hidden-tel-input'
43419             };
43420             
43421             if (this.name) {
43422                 hiddenInput.name = this.name;
43423             }
43424             
43425             if (this.disabled) {
43426                 input.disabled = true;
43427             }
43428             
43429             var flag_container = {
43430                 tag: 'div',
43431                 cls: 'flag-box',
43432                 cn: [
43433                     {
43434                         tag: 'div',
43435                         cls: 'flag'
43436                     },
43437                     {
43438                         tag: 'div',
43439                         cls: 'caret'
43440                     }
43441                 ]
43442             };
43443             
43444             var box = {
43445                 tag: 'div',
43446                 cls: this.hasFeedback ? 'has-feedback' : '',
43447                 cn: [
43448                     hiddenInput,
43449                     input,
43450                     {
43451                         tag: 'input',
43452                         cls: 'dial-code-holder',
43453                         disabled: true
43454                     }
43455                 ]
43456             };
43457             
43458             var container = {
43459                 cls: 'roo-select2-container input-group',
43460                 cn: [
43461                     flag_container,
43462                     box
43463                 ]
43464             };
43465             
43466             if (this.fieldLabel.length) {
43467                 var indicator = {
43468                     tag: 'i',
43469                     tooltip: 'This field is required'
43470                 };
43471                 
43472                 var label = {
43473                     tag: 'label',
43474                     'for':  id,
43475                     cls: 'control-label',
43476                     cn: []
43477                 };
43478                 
43479                 var label_text = {
43480                     tag: 'span',
43481                     html: this.fieldLabel
43482                 };
43483                 
43484                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43485                 label.cn = [
43486                     indicator,
43487                     label_text
43488                 ];
43489                 
43490                 if(this.indicatorpos == 'right') {
43491                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43492                     label.cn = [
43493                         label_text,
43494                         indicator
43495                     ];
43496                 }
43497                 
43498                 if(align == 'left') {
43499                     container = {
43500                         tag: 'div',
43501                         cn: [
43502                             container
43503                         ]
43504                     };
43505                     
43506                     if(this.labelWidth > 12){
43507                         label.style = "width: " + this.labelWidth + 'px';
43508                     }
43509                     if(this.labelWidth < 13 && this.labelmd == 0){
43510                         this.labelmd = this.labelWidth;
43511                     }
43512                     if(this.labellg > 0){
43513                         label.cls += ' col-lg-' + this.labellg;
43514                         input.cls += ' col-lg-' + (12 - this.labellg);
43515                     }
43516                     if(this.labelmd > 0){
43517                         label.cls += ' col-md-' + this.labelmd;
43518                         container.cls += ' col-md-' + (12 - this.labelmd);
43519                     }
43520                     if(this.labelsm > 0){
43521                         label.cls += ' col-sm-' + this.labelsm;
43522                         container.cls += ' col-sm-' + (12 - this.labelsm);
43523                     }
43524                     if(this.labelxs > 0){
43525                         label.cls += ' col-xs-' + this.labelxs;
43526                         container.cls += ' col-xs-' + (12 - this.labelxs);
43527                     }
43528                 }
43529             }
43530             
43531             cfg.cn = [
43532                 label,
43533                 container
43534             ];
43535             
43536             var settings = this;
43537             
43538             ['xs','sm','md','lg'].map(function(size){
43539                 if (settings[size]) {
43540                     cfg.cls += ' col-' + size + '-' + settings[size];
43541                 }
43542             });
43543             
43544             this.store = new Roo.data.Store({
43545                 proxy : new Roo.data.MemoryProxy({}),
43546                 reader : new Roo.data.JsonReader({
43547                     fields : [
43548                         {
43549                             'name' : 'name',
43550                             'type' : 'string'
43551                         },
43552                         {
43553                             'name' : 'iso2',
43554                             'type' : 'string'
43555                         },
43556                         {
43557                             'name' : 'dialCode',
43558                             'type' : 'string'
43559                         },
43560                         {
43561                             'name' : 'priority',
43562                             'type' : 'string'
43563                         },
43564                         {
43565                             'name' : 'areaCodes',
43566                             'type' : 'string'
43567                         }
43568                     ]
43569                 })
43570             });
43571             
43572             if(!this.preferedCountries) {
43573                 this.preferedCountries = [
43574                     'hk',
43575                     'gb',
43576                     'us'
43577                 ];
43578             }
43579             
43580             var p = this.preferedCountries.reverse();
43581             
43582             if(p) {
43583                 for (var i = 0; i < p.length; i++) {
43584                     for (var j = 0; j < this.allCountries.length; j++) {
43585                         if(this.allCountries[j].iso2 == p[i]) {
43586                             var t = this.allCountries[j];
43587                             this.allCountries.splice(j,1);
43588                             this.allCountries.unshift(t);
43589                         }
43590                     } 
43591                 }
43592             }
43593             
43594             this.store.proxy.data = {
43595                 success: true,
43596                 data: this.allCountries
43597             };
43598             
43599             return cfg;
43600         },
43601         
43602         initEvents : function()
43603         {
43604             this.createList();
43605             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43606             
43607             this.indicator = this.indicatorEl();
43608             this.flag = this.flagEl();
43609             this.dialCodeHolder = this.dialCodeHolderEl();
43610             
43611             this.trigger = this.el.select('div.flag-box',true).first();
43612             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43613             
43614             var _this = this;
43615             
43616             (function(){
43617                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43618                 _this.list.setWidth(lw);
43619             }).defer(100);
43620             
43621             this.list.on('mouseover', this.onViewOver, this);
43622             this.list.on('mousemove', this.onViewMove, this);
43623             this.inputEl().on("keyup", this.onKeyUp, this);
43624             this.inputEl().on("keypress", this.onKeyPress, this);
43625             
43626             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43627
43628             this.view = new Roo.View(this.list, this.tpl, {
43629                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43630             });
43631             
43632             this.view.on('click', this.onViewClick, this);
43633             this.setValue(this.defaultDialCode);
43634         },
43635         
43636         onTriggerClick : function(e)
43637         {
43638             Roo.log('trigger click');
43639             if(this.disabled){
43640                 return;
43641             }
43642             
43643             if(this.isExpanded()){
43644                 this.collapse();
43645                 this.hasFocus = false;
43646             }else {
43647                 this.store.load({});
43648                 this.hasFocus = true;
43649                 this.expand();
43650             }
43651         },
43652         
43653         isExpanded : function()
43654         {
43655             return this.list.isVisible();
43656         },
43657         
43658         collapse : function()
43659         {
43660             if(!this.isExpanded()){
43661                 return;
43662             }
43663             this.list.hide();
43664             Roo.get(document).un('mousedown', this.collapseIf, this);
43665             Roo.get(document).un('mousewheel', this.collapseIf, this);
43666             this.fireEvent('collapse', this);
43667             this.validate();
43668         },
43669         
43670         expand : function()
43671         {
43672             Roo.log('expand');
43673
43674             if(this.isExpanded() || !this.hasFocus){
43675                 return;
43676             }
43677             
43678             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43679             this.list.setWidth(lw);
43680             
43681             this.list.show();
43682             this.restrictHeight();
43683             
43684             Roo.get(document).on('mousedown', this.collapseIf, this);
43685             Roo.get(document).on('mousewheel', this.collapseIf, this);
43686             
43687             this.fireEvent('expand', this);
43688         },
43689         
43690         restrictHeight : function()
43691         {
43692             this.list.alignTo(this.inputEl(), this.listAlign);
43693             this.list.alignTo(this.inputEl(), this.listAlign);
43694         },
43695         
43696         onViewOver : function(e, t)
43697         {
43698             if(this.inKeyMode){
43699                 return;
43700             }
43701             var item = this.view.findItemFromChild(t);
43702             
43703             if(item){
43704                 var index = this.view.indexOf(item);
43705                 this.select(index, false);
43706             }
43707         },
43708
43709         // private
43710         onViewClick : function(view, doFocus, el, e)
43711         {
43712             var index = this.view.getSelectedIndexes()[0];
43713             
43714             var r = this.store.getAt(index);
43715             
43716             if(r){
43717                 this.onSelect(r, index);
43718             }
43719             if(doFocus !== false && !this.blockFocus){
43720                 this.inputEl().focus();
43721             }
43722         },
43723         
43724         onViewMove : function(e, t)
43725         {
43726             this.inKeyMode = false;
43727         },
43728         
43729         select : function(index, scrollIntoView)
43730         {
43731             this.selectedIndex = index;
43732             this.view.select(index);
43733             if(scrollIntoView !== false){
43734                 var el = this.view.getNode(index);
43735                 if(el){
43736                     this.list.scrollChildIntoView(el, false);
43737                 }
43738             }
43739         },
43740         
43741         createList : function()
43742         {
43743             this.list = Roo.get(document.body).createChild({
43744                 tag: 'ul',
43745                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43746                 style: 'display:none'
43747             });
43748             
43749             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43750         },
43751         
43752         collapseIf : function(e)
43753         {
43754             var in_combo  = e.within(this.el);
43755             var in_list =  e.within(this.list);
43756             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43757             
43758             if (in_combo || in_list || is_list) {
43759                 return;
43760             }
43761             this.collapse();
43762         },
43763         
43764         onSelect : function(record, index)
43765         {
43766             if(this.fireEvent('beforeselect', this, record, index) !== false){
43767                 
43768                 this.setFlagClass(record.data.iso2);
43769                 this.setDialCode(record.data.dialCode);
43770                 this.hasFocus = false;
43771                 this.collapse();
43772                 this.fireEvent('select', this, record, index);
43773             }
43774         },
43775         
43776         flagEl : function()
43777         {
43778             var flag = this.el.select('div.flag',true).first();
43779             if(!flag){
43780                 return false;
43781             }
43782             return flag;
43783         },
43784         
43785         dialCodeHolderEl : function()
43786         {
43787             var d = this.el.select('input.dial-code-holder',true).first();
43788             if(!d){
43789                 return false;
43790             }
43791             return d;
43792         },
43793         
43794         setDialCode : function(v)
43795         {
43796             this.dialCodeHolder.dom.value = '+'+v;
43797         },
43798         
43799         setFlagClass : function(n)
43800         {
43801             this.flag.dom.className = 'flag '+n;
43802         },
43803         
43804         getValue : function()
43805         {
43806             var v = this.inputEl().getValue();
43807             if(this.dialCodeHolder) {
43808                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43809             }
43810             return v;
43811         },
43812         
43813         setValue : function(v)
43814         {
43815             var d = this.getDialCode(v);
43816             
43817             //invalid dial code
43818             if(v.length == 0 || !d || d.length == 0) {
43819                 if(this.rendered){
43820                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43821                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43822                 }
43823                 return;
43824             }
43825             
43826             //valid dial code
43827             this.setFlagClass(this.dialCodeMapping[d].iso2);
43828             this.setDialCode(d);
43829             this.inputEl().dom.value = v.replace('+'+d,'');
43830             this.hiddenEl().dom.value = this.getValue();
43831             
43832             this.validate();
43833         },
43834         
43835         getDialCode : function(v)
43836         {
43837             v = v ||  '';
43838             
43839             if (v.length == 0) {
43840                 return this.dialCodeHolder.dom.value;
43841             }
43842             
43843             var dialCode = "";
43844             if (v.charAt(0) != "+") {
43845                 return false;
43846             }
43847             var numericChars = "";
43848             for (var i = 1; i < v.length; i++) {
43849               var c = v.charAt(i);
43850               if (!isNaN(c)) {
43851                 numericChars += c;
43852                 if (this.dialCodeMapping[numericChars]) {
43853                   dialCode = v.substr(1, i);
43854                 }
43855                 if (numericChars.length == 4) {
43856                   break;
43857                 }
43858               }
43859             }
43860             return dialCode;
43861         },
43862         
43863         reset : function()
43864         {
43865             this.setValue(this.defaultDialCode);
43866             this.validate();
43867         },
43868         
43869         hiddenEl : function()
43870         {
43871             return this.el.select('input.hidden-tel-input',true).first();
43872         },
43873         
43874         // after setting val
43875         onKeyUp : function(e){
43876             this.setValue(this.getValue());
43877         },
43878         
43879         onKeyPress : function(e){
43880             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43881                 e.stopEvent();
43882             }
43883         }
43884         
43885 });
43886 /**
43887  * @class Roo.bootstrap.form.MoneyField
43888  * @extends Roo.bootstrap.form.ComboBox
43889  * Bootstrap MoneyField class
43890  * 
43891  * @constructor
43892  * Create a new MoneyField.
43893  * @param {Object} config Configuration options
43894  */
43895
43896 Roo.bootstrap.form.MoneyField = function(config) {
43897     
43898     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43899     
43900 };
43901
43902 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43903     
43904     /**
43905      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43906      */
43907     allowDecimals : true,
43908     /**
43909      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43910      */
43911     decimalSeparator : ".",
43912     /**
43913      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43914      */
43915     decimalPrecision : 0,
43916     /**
43917      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43918      */
43919     allowNegative : true,
43920     /**
43921      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43922      */
43923     allowZero: true,
43924     /**
43925      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43926      */
43927     minValue : Number.NEGATIVE_INFINITY,
43928     /**
43929      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43930      */
43931     maxValue : Number.MAX_VALUE,
43932     /**
43933      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43934      */
43935     minText : "The minimum value for this field is {0}",
43936     /**
43937      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43938      */
43939     maxText : "The maximum value for this field is {0}",
43940     /**
43941      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43942      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43943      */
43944     nanText : "{0} is not a valid number",
43945     /**
43946      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43947      */
43948     castInt : true,
43949     /**
43950      * @cfg {String} defaults currency of the MoneyField
43951      * value should be in lkey
43952      */
43953     defaultCurrency : false,
43954     /**
43955      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43956      */
43957     thousandsDelimiter : false,
43958     /**
43959      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43960      */
43961     max_length: false,
43962     
43963     inputlg : 9,
43964     inputmd : 9,
43965     inputsm : 9,
43966     inputxs : 6,
43967      /**
43968      * @cfg {Roo.data.Store} store  Store to lookup currency??
43969      */
43970     store : false,
43971     
43972     getAutoCreate : function()
43973     {
43974         var align = this.labelAlign || this.parentLabelAlign();
43975         
43976         var id = Roo.id();
43977
43978         var cfg = {
43979             cls: 'form-group',
43980             cn: []
43981         };
43982
43983         var input =  {
43984             tag: 'input',
43985             id : id,
43986             cls : 'form-control roo-money-amount-input',
43987             autocomplete: 'new-password'
43988         };
43989         
43990         var hiddenInput = {
43991             tag: 'input',
43992             type: 'hidden',
43993             id: Roo.id(),
43994             cls: 'hidden-number-input'
43995         };
43996         
43997         if(this.max_length) {
43998             input.maxlength = this.max_length; 
43999         }
44000         
44001         if (this.name) {
44002             hiddenInput.name = this.name;
44003         }
44004
44005         if (this.disabled) {
44006             input.disabled = true;
44007         }
44008
44009         var clg = 12 - this.inputlg;
44010         var cmd = 12 - this.inputmd;
44011         var csm = 12 - this.inputsm;
44012         var cxs = 12 - this.inputxs;
44013         
44014         var container = {
44015             tag : 'div',
44016             cls : 'row roo-money-field',
44017             cn : [
44018                 {
44019                     tag : 'div',
44020                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44021                     cn : [
44022                         {
44023                             tag : 'div',
44024                             cls: 'roo-select2-container input-group',
44025                             cn: [
44026                                 {
44027                                     tag : 'input',
44028                                     cls : 'form-control roo-money-currency-input',
44029                                     autocomplete: 'new-password',
44030                                     readOnly : 1,
44031                                     name : this.currencyName
44032                                 },
44033                                 {
44034                                     tag :'span',
44035                                     cls : 'input-group-addon',
44036                                     cn : [
44037                                         {
44038                                             tag: 'span',
44039                                             cls: 'caret'
44040                                         }
44041                                     ]
44042                                 }
44043                             ]
44044                         }
44045                     ]
44046                 },
44047                 {
44048                     tag : 'div',
44049                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44050                     cn : [
44051                         {
44052                             tag: 'div',
44053                             cls: this.hasFeedback ? 'has-feedback' : '',
44054                             cn: [
44055                                 input
44056                             ]
44057                         }
44058                     ]
44059                 }
44060             ]
44061             
44062         };
44063         
44064         if (this.fieldLabel.length) {
44065             var indicator = {
44066                 tag: 'i',
44067                 tooltip: 'This field is required'
44068             };
44069
44070             var label = {
44071                 tag: 'label',
44072                 'for':  id,
44073                 cls: 'control-label',
44074                 cn: []
44075             };
44076
44077             var label_text = {
44078                 tag: 'span',
44079                 html: this.fieldLabel
44080             };
44081
44082             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44083             label.cn = [
44084                 indicator,
44085                 label_text
44086             ];
44087
44088             if(this.indicatorpos == 'right') {
44089                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44090                 label.cn = [
44091                     label_text,
44092                     indicator
44093                 ];
44094             }
44095
44096             if(align == 'left') {
44097                 container = {
44098                     tag: 'div',
44099                     cn: [
44100                         container
44101                     ]
44102                 };
44103
44104                 if(this.labelWidth > 12){
44105                     label.style = "width: " + this.labelWidth + 'px';
44106                 }
44107                 if(this.labelWidth < 13 && this.labelmd == 0){
44108                     this.labelmd = this.labelWidth;
44109                 }
44110                 if(this.labellg > 0){
44111                     label.cls += ' col-lg-' + this.labellg;
44112                     input.cls += ' col-lg-' + (12 - this.labellg);
44113                 }
44114                 if(this.labelmd > 0){
44115                     label.cls += ' col-md-' + this.labelmd;
44116                     container.cls += ' col-md-' + (12 - this.labelmd);
44117                 }
44118                 if(this.labelsm > 0){
44119                     label.cls += ' col-sm-' + this.labelsm;
44120                     container.cls += ' col-sm-' + (12 - this.labelsm);
44121                 }
44122                 if(this.labelxs > 0){
44123                     label.cls += ' col-xs-' + this.labelxs;
44124                     container.cls += ' col-xs-' + (12 - this.labelxs);
44125                 }
44126             }
44127         }
44128
44129         cfg.cn = [
44130             label,
44131             container,
44132             hiddenInput
44133         ];
44134         
44135         var settings = this;
44136
44137         ['xs','sm','md','lg'].map(function(size){
44138             if (settings[size]) {
44139                 cfg.cls += ' col-' + size + '-' + settings[size];
44140             }
44141         });
44142         
44143         return cfg;
44144     },
44145     
44146     initEvents : function()
44147     {
44148         this.indicator = this.indicatorEl();
44149         
44150         this.initCurrencyEvent();
44151         
44152         this.initNumberEvent();
44153     },
44154     
44155     initCurrencyEvent : function()
44156     {
44157         if (!this.store) {
44158             throw "can not find store for combo";
44159         }
44160         
44161         this.store = Roo.factory(this.store, Roo.data);
44162         this.store.parent = this;
44163         
44164         this.createList();
44165         
44166         this.triggerEl = this.el.select('.input-group-addon', true).first();
44167         
44168         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44169         
44170         var _this = this;
44171         
44172         (function(){
44173             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44174             _this.list.setWidth(lw);
44175         }).defer(100);
44176         
44177         this.list.on('mouseover', this.onViewOver, this);
44178         this.list.on('mousemove', this.onViewMove, this);
44179         this.list.on('scroll', this.onViewScroll, this);
44180         
44181         if(!this.tpl){
44182             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44183         }
44184         
44185         this.view = new Roo.View(this.list, this.tpl, {
44186             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44187         });
44188         
44189         this.view.on('click', this.onViewClick, this);
44190         
44191         this.store.on('beforeload', this.onBeforeLoad, this);
44192         this.store.on('load', this.onLoad, this);
44193         this.store.on('loadexception', this.onLoadException, this);
44194         
44195         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44196             "up" : function(e){
44197                 this.inKeyMode = true;
44198                 this.selectPrev();
44199             },
44200
44201             "down" : function(e){
44202                 if(!this.isExpanded()){
44203                     this.onTriggerClick();
44204                 }else{
44205                     this.inKeyMode = true;
44206                     this.selectNext();
44207                 }
44208             },
44209
44210             "enter" : function(e){
44211                 this.collapse();
44212                 
44213                 if(this.fireEvent("specialkey", this, e)){
44214                     this.onViewClick(false);
44215                 }
44216                 
44217                 return true;
44218             },
44219
44220             "esc" : function(e){
44221                 this.collapse();
44222             },
44223
44224             "tab" : function(e){
44225                 this.collapse();
44226                 
44227                 if(this.fireEvent("specialkey", this, e)){
44228                     this.onViewClick(false);
44229                 }
44230                 
44231                 return true;
44232             },
44233
44234             scope : this,
44235
44236             doRelay : function(foo, bar, hname){
44237                 if(hname == 'down' || this.scope.isExpanded()){
44238                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44239                 }
44240                 return true;
44241             },
44242
44243             forceKeyDown: true
44244         });
44245         
44246         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44247         
44248     },
44249     
44250     initNumberEvent : function(e)
44251     {
44252         this.inputEl().on("keydown" , this.fireKey,  this);
44253         this.inputEl().on("focus", this.onFocus,  this);
44254         this.inputEl().on("blur", this.onBlur,  this);
44255         
44256         this.inputEl().relayEvent('keyup', this);
44257         
44258         if(this.indicator){
44259             this.indicator.addClass('invisible');
44260         }
44261  
44262         this.originalValue = this.getValue();
44263         
44264         if(this.validationEvent == 'keyup'){
44265             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44266             this.inputEl().on('keyup', this.filterValidation, this);
44267         }
44268         else if(this.validationEvent !== false){
44269             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44270         }
44271         
44272         if(this.selectOnFocus){
44273             this.on("focus", this.preFocus, this);
44274             
44275         }
44276         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44277             this.inputEl().on("keypress", this.filterKeys, this);
44278         } else {
44279             this.inputEl().relayEvent('keypress', this);
44280         }
44281         
44282         var allowed = "0123456789";
44283         
44284         if(this.allowDecimals){
44285             allowed += this.decimalSeparator;
44286         }
44287         
44288         if(this.allowNegative){
44289             allowed += "-";
44290         }
44291         
44292         if(this.thousandsDelimiter) {
44293             allowed += ",";
44294         }
44295         
44296         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44297         
44298         var keyPress = function(e){
44299             
44300             var k = e.getKey();
44301             
44302             var c = e.getCharCode();
44303             
44304             if(
44305                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44306                     allowed.indexOf(String.fromCharCode(c)) === -1
44307             ){
44308                 e.stopEvent();
44309                 return;
44310             }
44311             
44312             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44313                 return;
44314             }
44315             
44316             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44317                 e.stopEvent();
44318             }
44319         };
44320         
44321         this.inputEl().on("keypress", keyPress, this);
44322         
44323     },
44324     
44325     onTriggerClick : function(e)
44326     {   
44327         if(this.disabled){
44328             return;
44329         }
44330         
44331         this.page = 0;
44332         this.loadNext = false;
44333         
44334         if(this.isExpanded()){
44335             this.collapse();
44336             return;
44337         }
44338         
44339         this.hasFocus = true;
44340         
44341         if(this.triggerAction == 'all') {
44342             this.doQuery(this.allQuery, true);
44343             return;
44344         }
44345         
44346         this.doQuery(this.getRawValue());
44347     },
44348     
44349     getCurrency : function()
44350     {   
44351         var v = this.currencyEl().getValue();
44352         
44353         return v;
44354     },
44355     
44356     restrictHeight : function()
44357     {
44358         this.list.alignTo(this.currencyEl(), this.listAlign);
44359         this.list.alignTo(this.currencyEl(), this.listAlign);
44360     },
44361     
44362     onViewClick : function(view, doFocus, el, e)
44363     {
44364         var index = this.view.getSelectedIndexes()[0];
44365         
44366         var r = this.store.getAt(index);
44367         
44368         if(r){
44369             this.onSelect(r, index);
44370         }
44371     },
44372     
44373     onSelect : function(record, index){
44374         
44375         if(this.fireEvent('beforeselect', this, record, index) !== false){
44376         
44377             this.setFromCurrencyData(index > -1 ? record.data : false);
44378             
44379             this.collapse();
44380             
44381             this.fireEvent('select', this, record, index);
44382         }
44383     },
44384     
44385     setFromCurrencyData : function(o)
44386     {
44387         var currency = '';
44388         
44389         this.lastCurrency = o;
44390         
44391         if (this.currencyField) {
44392             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44393         } else {
44394             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44395         }
44396         
44397         this.lastSelectionText = currency;
44398         
44399         //setting default currency
44400         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44401             this.setCurrency(this.defaultCurrency);
44402             return;
44403         }
44404         
44405         this.setCurrency(currency);
44406     },
44407     
44408     setFromData : function(o)
44409     {
44410         var c = {};
44411         
44412         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44413         
44414         this.setFromCurrencyData(c);
44415         
44416         var value = '';
44417         
44418         if (this.name) {
44419             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44420         } else {
44421             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44422         }
44423         
44424         this.setValue(value);
44425         
44426     },
44427     
44428     setCurrency : function(v)
44429     {   
44430         this.currencyValue = v;
44431         
44432         if(this.rendered){
44433             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44434             this.validate();
44435         }
44436     },
44437     
44438     setValue : function(v)
44439     {
44440         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44441         
44442         this.value = v;
44443         
44444         if(this.rendered){
44445             
44446             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44447             
44448             this.inputEl().dom.value = (v == '') ? '' :
44449                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44450             
44451             if(!this.allowZero && v === '0') {
44452                 this.hiddenEl().dom.value = '';
44453                 this.inputEl().dom.value = '';
44454             }
44455             
44456             this.validate();
44457         }
44458     },
44459     
44460     getRawValue : function()
44461     {
44462         var v = this.inputEl().getValue();
44463         
44464         return v;
44465     },
44466     
44467     getValue : function()
44468     {
44469         return this.fixPrecision(this.parseValue(this.getRawValue()));
44470     },
44471     
44472     parseValue : function(value)
44473     {
44474         if(this.thousandsDelimiter) {
44475             value += "";
44476             r = new RegExp(",", "g");
44477             value = value.replace(r, "");
44478         }
44479         
44480         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44481         return isNaN(value) ? '' : value;
44482         
44483     },
44484     
44485     fixPrecision : function(value)
44486     {
44487         if(this.thousandsDelimiter) {
44488             value += "";
44489             r = new RegExp(",", "g");
44490             value = value.replace(r, "");
44491         }
44492         
44493         var nan = isNaN(value);
44494         
44495         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44496             return nan ? '' : value;
44497         }
44498         return parseFloat(value).toFixed(this.decimalPrecision);
44499     },
44500     
44501     decimalPrecisionFcn : function(v)
44502     {
44503         return Math.floor(v);
44504     },
44505     
44506     validateValue : function(value)
44507     {
44508         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44509             return false;
44510         }
44511         
44512         var num = this.parseValue(value);
44513         
44514         if(isNaN(num)){
44515             this.markInvalid(String.format(this.nanText, value));
44516             return false;
44517         }
44518         
44519         if(num < this.minValue){
44520             this.markInvalid(String.format(this.minText, this.minValue));
44521             return false;
44522         }
44523         
44524         if(num > this.maxValue){
44525             this.markInvalid(String.format(this.maxText, this.maxValue));
44526             return false;
44527         }
44528         
44529         return true;
44530     },
44531     
44532     validate : function()
44533     {
44534         if(this.disabled || this.allowBlank){
44535             this.markValid();
44536             return true;
44537         }
44538         
44539         var currency = this.getCurrency();
44540         
44541         if(this.validateValue(this.getRawValue()) && currency.length){
44542             this.markValid();
44543             return true;
44544         }
44545         
44546         this.markInvalid();
44547         return false;
44548     },
44549     
44550     getName: function()
44551     {
44552         return this.name;
44553     },
44554     
44555     beforeBlur : function()
44556     {
44557         if(!this.castInt){
44558             return;
44559         }
44560         
44561         var v = this.parseValue(this.getRawValue());
44562         
44563         if(v || v == 0){
44564             this.setValue(v);
44565         }
44566     },
44567     
44568     onBlur : function()
44569     {
44570         this.beforeBlur();
44571         
44572         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44573             //this.el.removeClass(this.focusClass);
44574         }
44575         
44576         this.hasFocus = false;
44577         
44578         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44579             this.validate();
44580         }
44581         
44582         var v = this.getValue();
44583         
44584         if(String(v) !== String(this.startValue)){
44585             this.fireEvent('change', this, v, this.startValue);
44586         }
44587         
44588         this.fireEvent("blur", this);
44589     },
44590     
44591     inputEl : function()
44592     {
44593         return this.el.select('.roo-money-amount-input', true).first();
44594     },
44595     
44596     currencyEl : function()
44597     {
44598         return this.el.select('.roo-money-currency-input', true).first();
44599     },
44600     
44601     hiddenEl : function()
44602     {
44603         return this.el.select('input.hidden-number-input',true).first();
44604     }
44605     
44606 });/**
44607  * @class Roo.bootstrap.BezierSignature
44608  * @extends Roo.bootstrap.Component
44609  * Bootstrap BezierSignature class
44610  * This script refer to:
44611  *    Title: Signature Pad
44612  *    Author: szimek
44613  *    Availability: https://github.com/szimek/signature_pad
44614  *
44615  * @constructor
44616  * Create a new BezierSignature
44617  * @param {Object} config The config object
44618  */
44619
44620 Roo.bootstrap.BezierSignature = function(config){
44621     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44622     this.addEvents({
44623         "resize" : true
44624     });
44625 };
44626
44627 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44628 {
44629      
44630     curve_data: [],
44631     
44632     is_empty: true,
44633     
44634     mouse_btn_down: true,
44635     
44636     /**
44637      * @cfg {int} canvas height
44638      */
44639     canvas_height: '200px',
44640     
44641     /**
44642      * @cfg {float|function} Radius of a single dot.
44643      */ 
44644     dot_size: false,
44645     
44646     /**
44647      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44648      */
44649     min_width: 0.5,
44650     
44651     /**
44652      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44653      */
44654     max_width: 2.5,
44655     
44656     /**
44657      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44658      */
44659     throttle: 16,
44660     
44661     /**
44662      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44663      */
44664     min_distance: 5,
44665     
44666     /**
44667      * @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.
44668      */
44669     bg_color: 'rgba(0, 0, 0, 0)',
44670     
44671     /**
44672      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44673      */
44674     dot_color: 'black',
44675     
44676     /**
44677      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44678      */ 
44679     velocity_filter_weight: 0.7,
44680     
44681     /**
44682      * @cfg {function} Callback when stroke begin. 
44683      */
44684     onBegin: false,
44685     
44686     /**
44687      * @cfg {function} Callback when stroke end.
44688      */
44689     onEnd: false,
44690     
44691     getAutoCreate : function()
44692     {
44693         var cls = 'roo-signature column';
44694         
44695         if(this.cls){
44696             cls += ' ' + this.cls;
44697         }
44698         
44699         var col_sizes = [
44700             'lg',
44701             'md',
44702             'sm',
44703             'xs'
44704         ];
44705         
44706         for(var i = 0; i < col_sizes.length; i++) {
44707             if(this[col_sizes[i]]) {
44708                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44709             }
44710         }
44711         
44712         var cfg = {
44713             tag: 'div',
44714             cls: cls,
44715             cn: [
44716                 {
44717                     tag: 'div',
44718                     cls: 'roo-signature-body',
44719                     cn: [
44720                         {
44721                             tag: 'canvas',
44722                             cls: 'roo-signature-body-canvas',
44723                             height: this.canvas_height,
44724                             width: this.canvas_width
44725                         }
44726                     ]
44727                 },
44728                 {
44729                     tag: 'input',
44730                     type: 'file',
44731                     style: 'display: none'
44732                 }
44733             ]
44734         };
44735         
44736         return cfg;
44737     },
44738     
44739     initEvents: function() 
44740     {
44741         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44742         
44743         var canvas = this.canvasEl();
44744         
44745         // mouse && touch event swapping...
44746         canvas.dom.style.touchAction = 'none';
44747         canvas.dom.style.msTouchAction = 'none';
44748         
44749         this.mouse_btn_down = false;
44750         canvas.on('mousedown', this._handleMouseDown, this);
44751         canvas.on('mousemove', this._handleMouseMove, this);
44752         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44753         
44754         if (window.PointerEvent) {
44755             canvas.on('pointerdown', this._handleMouseDown, this);
44756             canvas.on('pointermove', this._handleMouseMove, this);
44757             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44758         }
44759         
44760         if ('ontouchstart' in window) {
44761             canvas.on('touchstart', this._handleTouchStart, this);
44762             canvas.on('touchmove', this._handleTouchMove, this);
44763             canvas.on('touchend', this._handleTouchEnd, this);
44764         }
44765         
44766         Roo.EventManager.onWindowResize(this.resize, this, true);
44767         
44768         // file input event
44769         this.fileEl().on('change', this.uploadImage, this);
44770         
44771         this.clear();
44772         
44773         this.resize();
44774     },
44775     
44776     resize: function(){
44777         
44778         var canvas = this.canvasEl().dom;
44779         var ctx = this.canvasElCtx();
44780         var img_data = false;
44781         
44782         if(canvas.width > 0) {
44783             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44784         }
44785         // setting canvas width will clean img data
44786         canvas.width = 0;
44787         
44788         var style = window.getComputedStyle ? 
44789             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44790             
44791         var padding_left = parseInt(style.paddingLeft) || 0;
44792         var padding_right = parseInt(style.paddingRight) || 0;
44793         
44794         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44795         
44796         if(img_data) {
44797             ctx.putImageData(img_data, 0, 0);
44798         }
44799     },
44800     
44801     _handleMouseDown: function(e)
44802     {
44803         if (e.browserEvent.which === 1) {
44804             this.mouse_btn_down = true;
44805             this.strokeBegin(e);
44806         }
44807     },
44808     
44809     _handleMouseMove: function (e)
44810     {
44811         if (this.mouse_btn_down) {
44812             this.strokeMoveUpdate(e);
44813         }
44814     },
44815     
44816     _handleMouseUp: function (e)
44817     {
44818         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44819             this.mouse_btn_down = false;
44820             this.strokeEnd(e);
44821         }
44822     },
44823     
44824     _handleTouchStart: function (e) {
44825         
44826         e.preventDefault();
44827         if (e.browserEvent.targetTouches.length === 1) {
44828             // var touch = e.browserEvent.changedTouches[0];
44829             // this.strokeBegin(touch);
44830             
44831              this.strokeBegin(e); // assume e catching the correct xy...
44832         }
44833     },
44834     
44835     _handleTouchMove: function (e) {
44836         e.preventDefault();
44837         // var touch = event.targetTouches[0];
44838         // _this._strokeMoveUpdate(touch);
44839         this.strokeMoveUpdate(e);
44840     },
44841     
44842     _handleTouchEnd: function (e) {
44843         var wasCanvasTouched = e.target === this.canvasEl().dom;
44844         if (wasCanvasTouched) {
44845             e.preventDefault();
44846             // var touch = event.changedTouches[0];
44847             // _this._strokeEnd(touch);
44848             this.strokeEnd(e);
44849         }
44850     },
44851     
44852     reset: function () {
44853         this._lastPoints = [];
44854         this._lastVelocity = 0;
44855         this._lastWidth = (this.min_width + this.max_width) / 2;
44856         this.canvasElCtx().fillStyle = this.dot_color;
44857     },
44858     
44859     strokeMoveUpdate: function(e)
44860     {
44861         this.strokeUpdate(e);
44862         
44863         if (this.throttle) {
44864             this.throttleStroke(this.strokeUpdate, this.throttle);
44865         }
44866         else {
44867             this.strokeUpdate(e);
44868         }
44869     },
44870     
44871     strokeBegin: function(e)
44872     {
44873         var newPointGroup = {
44874             color: this.dot_color,
44875             points: []
44876         };
44877         
44878         if (typeof this.onBegin === 'function') {
44879             this.onBegin(e);
44880         }
44881         
44882         this.curve_data.push(newPointGroup);
44883         this.reset();
44884         this.strokeUpdate(e);
44885     },
44886     
44887     strokeUpdate: function(e)
44888     {
44889         var rect = this.canvasEl().dom.getBoundingClientRect();
44890         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44891         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44892         var lastPoints = lastPointGroup.points;
44893         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44894         var isLastPointTooClose = lastPoint
44895             ? point.distanceTo(lastPoint) <= this.min_distance
44896             : false;
44897         var color = lastPointGroup.color;
44898         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44899             var curve = this.addPoint(point);
44900             if (!lastPoint) {
44901                 this.drawDot({color: color, point: point});
44902             }
44903             else if (curve) {
44904                 this.drawCurve({color: color, curve: curve});
44905             }
44906             lastPoints.push({
44907                 time: point.time,
44908                 x: point.x,
44909                 y: point.y
44910             });
44911         }
44912     },
44913     
44914     strokeEnd: function(e)
44915     {
44916         this.strokeUpdate(e);
44917         if (typeof this.onEnd === 'function') {
44918             this.onEnd(e);
44919         }
44920     },
44921     
44922     addPoint:  function (point) {
44923         var _lastPoints = this._lastPoints;
44924         _lastPoints.push(point);
44925         if (_lastPoints.length > 2) {
44926             if (_lastPoints.length === 3) {
44927                 _lastPoints.unshift(_lastPoints[0]);
44928             }
44929             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44930             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44931             _lastPoints.shift();
44932             return curve;
44933         }
44934         return null;
44935     },
44936     
44937     calculateCurveWidths: function (startPoint, endPoint) {
44938         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44939             (1 - this.velocity_filter_weight) * this._lastVelocity;
44940
44941         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44942         var widths = {
44943             end: newWidth,
44944             start: this._lastWidth
44945         };
44946         
44947         this._lastVelocity = velocity;
44948         this._lastWidth = newWidth;
44949         return widths;
44950     },
44951     
44952     drawDot: function (_a) {
44953         var color = _a.color, point = _a.point;
44954         var ctx = this.canvasElCtx();
44955         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44956         ctx.beginPath();
44957         this.drawCurveSegment(point.x, point.y, width);
44958         ctx.closePath();
44959         ctx.fillStyle = color;
44960         ctx.fill();
44961     },
44962     
44963     drawCurve: function (_a) {
44964         var color = _a.color, curve = _a.curve;
44965         var ctx = this.canvasElCtx();
44966         var widthDelta = curve.endWidth - curve.startWidth;
44967         var drawSteps = Math.floor(curve.length()) * 2;
44968         ctx.beginPath();
44969         ctx.fillStyle = color;
44970         for (var i = 0; i < drawSteps; i += 1) {
44971         var t = i / drawSteps;
44972         var tt = t * t;
44973         var ttt = tt * t;
44974         var u = 1 - t;
44975         var uu = u * u;
44976         var uuu = uu * u;
44977         var x = uuu * curve.startPoint.x;
44978         x += 3 * uu * t * curve.control1.x;
44979         x += 3 * u * tt * curve.control2.x;
44980         x += ttt * curve.endPoint.x;
44981         var y = uuu * curve.startPoint.y;
44982         y += 3 * uu * t * curve.control1.y;
44983         y += 3 * u * tt * curve.control2.y;
44984         y += ttt * curve.endPoint.y;
44985         var width = curve.startWidth + ttt * widthDelta;
44986         this.drawCurveSegment(x, y, width);
44987         }
44988         ctx.closePath();
44989         ctx.fill();
44990     },
44991     
44992     drawCurveSegment: function (x, y, width) {
44993         var ctx = this.canvasElCtx();
44994         ctx.moveTo(x, y);
44995         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44996         this.is_empty = false;
44997     },
44998     
44999     clear: function()
45000     {
45001         var ctx = this.canvasElCtx();
45002         var canvas = this.canvasEl().dom;
45003         ctx.fillStyle = this.bg_color;
45004         ctx.clearRect(0, 0, canvas.width, canvas.height);
45005         ctx.fillRect(0, 0, canvas.width, canvas.height);
45006         this.curve_data = [];
45007         this.reset();
45008         this.is_empty = true;
45009     },
45010     
45011     fileEl: function()
45012     {
45013         return  this.el.select('input',true).first();
45014     },
45015     
45016     canvasEl: function()
45017     {
45018         return this.el.select('canvas',true).first();
45019     },
45020     
45021     canvasElCtx: function()
45022     {
45023         return this.el.select('canvas',true).first().dom.getContext('2d');
45024     },
45025     
45026     getImage: function(type)
45027     {
45028         if(this.is_empty) {
45029             return false;
45030         }
45031         
45032         // encryption ?
45033         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45034     },
45035     
45036     drawFromImage: function(img_src)
45037     {
45038         var img = new Image();
45039         
45040         img.onload = function(){
45041             this.canvasElCtx().drawImage(img, 0, 0);
45042         }.bind(this);
45043         
45044         img.src = img_src;
45045         
45046         this.is_empty = false;
45047     },
45048     
45049     selectImage: function()
45050     {
45051         this.fileEl().dom.click();
45052     },
45053     
45054     uploadImage: function(e)
45055     {
45056         var reader = new FileReader();
45057         
45058         reader.onload = function(e){
45059             var img = new Image();
45060             img.onload = function(){
45061                 this.reset();
45062                 this.canvasElCtx().drawImage(img, 0, 0);
45063             }.bind(this);
45064             img.src = e.target.result;
45065         }.bind(this);
45066         
45067         reader.readAsDataURL(e.target.files[0]);
45068     },
45069     
45070     // Bezier Point Constructor
45071     Point: (function () {
45072         function Point(x, y, time) {
45073             this.x = x;
45074             this.y = y;
45075             this.time = time || Date.now();
45076         }
45077         Point.prototype.distanceTo = function (start) {
45078             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45079         };
45080         Point.prototype.equals = function (other) {
45081             return this.x === other.x && this.y === other.y && this.time === other.time;
45082         };
45083         Point.prototype.velocityFrom = function (start) {
45084             return this.time !== start.time
45085             ? this.distanceTo(start) / (this.time - start.time)
45086             : 0;
45087         };
45088         return Point;
45089     }()),
45090     
45091     
45092     // Bezier Constructor
45093     Bezier: (function () {
45094         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45095             this.startPoint = startPoint;
45096             this.control2 = control2;
45097             this.control1 = control1;
45098             this.endPoint = endPoint;
45099             this.startWidth = startWidth;
45100             this.endWidth = endWidth;
45101         }
45102         Bezier.fromPoints = function (points, widths, scope) {
45103             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45104             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45105             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45106         };
45107         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45108             var dx1 = s1.x - s2.x;
45109             var dy1 = s1.y - s2.y;
45110             var dx2 = s2.x - s3.x;
45111             var dy2 = s2.y - s3.y;
45112             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45113             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45114             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45115             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45116             var dxm = m1.x - m2.x;
45117             var dym = m1.y - m2.y;
45118             var k = l2 / (l1 + l2);
45119             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45120             var tx = s2.x - cm.x;
45121             var ty = s2.y - cm.y;
45122             return {
45123                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45124                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45125             };
45126         };
45127         Bezier.prototype.length = function () {
45128             var steps = 10;
45129             var length = 0;
45130             var px;
45131             var py;
45132             for (var i = 0; i <= steps; i += 1) {
45133                 var t = i / steps;
45134                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45135                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45136                 if (i > 0) {
45137                     var xdiff = cx - px;
45138                     var ydiff = cy - py;
45139                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45140                 }
45141                 px = cx;
45142                 py = cy;
45143             }
45144             return length;
45145         };
45146         Bezier.prototype.point = function (t, start, c1, c2, end) {
45147             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45148             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45149             + (3.0 * c2 * (1.0 - t) * t * t)
45150             + (end * t * t * t);
45151         };
45152         return Bezier;
45153     }()),
45154     
45155     throttleStroke: function(fn, wait) {
45156       if (wait === void 0) { wait = 250; }
45157       var previous = 0;
45158       var timeout = null;
45159       var result;
45160       var storedContext;
45161       var storedArgs;
45162       var later = function () {
45163           previous = Date.now();
45164           timeout = null;
45165           result = fn.apply(storedContext, storedArgs);
45166           if (!timeout) {
45167               storedContext = null;
45168               storedArgs = [];
45169           }
45170       };
45171       return function wrapper() {
45172           var args = [];
45173           for (var _i = 0; _i < arguments.length; _i++) {
45174               args[_i] = arguments[_i];
45175           }
45176           var now = Date.now();
45177           var remaining = wait - (now - previous);
45178           storedContext = this;
45179           storedArgs = args;
45180           if (remaining <= 0 || remaining > wait) {
45181               if (timeout) {
45182                   clearTimeout(timeout);
45183                   timeout = null;
45184               }
45185               previous = now;
45186               result = fn.apply(storedContext, storedArgs);
45187               if (!timeout) {
45188                   storedContext = null;
45189                   storedArgs = [];
45190               }
45191           }
45192           else if (!timeout) {
45193               timeout = window.setTimeout(later, remaining);
45194           }
45195           return result;
45196       };
45197   }
45198   
45199 });
45200
45201  
45202
45203  // old names for form elements
45204 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45205 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45206 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45207 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45208 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45209 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45210 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45211 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45212 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45213 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45214 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45215 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45216 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45217 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45218 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45219 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45220 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45221 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45222 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45223 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45224 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45225 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45226 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45227 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45228 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45229 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45230
45231 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45232 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45233
45234 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45235 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45236
45237 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45238 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45239 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45240 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45241