fix missing fields/meta data
[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.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938         var im = {
2939             tag: 'input',
2940             type : 'file',
2941             cls : 'd-none  roo-card-upload-selector' 
2942           
2943         };
2944         if (this.multiple) {
2945             im.multiple = 'multiple';
2946         }
2947         
2948         return  {
2949             cls :'div' ,
2950             cn : [
2951                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2952                 im
2953
2954             ]
2955         };
2956            
2957          
2958     },
2959      
2960    
2961     initEvents : function()
2962     {
2963         
2964         Roo.bootstrap.Button.prototype.initEvents.call(this);
2965         
2966         
2967         
2968         
2969         
2970         this.urlAPI = (window.createObjectURL && window) || 
2971                                 (window.URL && URL.revokeObjectURL && URL) || 
2972                                 (window.webkitURL && webkitURL);
2973                         
2974          
2975          
2976          
2977         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2978         
2979         this.selectorEl.on('change', this.onFileSelected, this);
2980          
2981          
2982        
2983     },
2984     
2985    
2986     onClick : function(e)
2987     {
2988         e.preventDefault();
2989         
2990         if ( this.fireEvent('beforeselect', this) === false) {
2991             return;
2992         }
2993          
2994         this.selectorEl.dom.click();
2995          
2996     },
2997     
2998     onFileSelected : function(e)
2999     {
3000         e.preventDefault();
3001         
3002         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003             return;
3004         }
3005         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006         this.selectorEl.dom.value  = '';// hopefully reset..
3007         
3008         this.fireEvent('uploaded', this,  files );
3009         
3010     },
3011     
3012        
3013    
3014     
3015     /**
3016      * addCard - add an Attachment to the uploader
3017      * @param data - the data about the image to upload
3018      *
3019      * {
3020           id : 123
3021           title : "Title of file",
3022           is_uploaded : false,
3023           src : "http://.....",
3024           srcfile : { the File upload object },
3025           mimetype : file.type,
3026           preview : false,
3027           is_deleted : 0
3028           .. any other data...
3029         }
3030      *
3031      * 
3032     */
3033      
3034     reset: function()
3035     {
3036          
3037          this.selectorEl
3038     } 
3039     
3040     
3041     
3042     
3043 });
3044  /*
3045  * - LGPL
3046  *
3047  * image
3048  * 
3049  */
3050
3051
3052 /**
3053  * @class Roo.bootstrap.Img
3054  * @extends Roo.bootstrap.Component
3055  * Bootstrap Img class
3056  * @cfg {Boolean} imgResponsive false | true
3057  * @cfg {String} border rounded | circle | thumbnail
3058  * @cfg {String} src image source
3059  * @cfg {String} alt image alternative text
3060  * @cfg {String} href a tag href
3061  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062  * @cfg {String} xsUrl xs image source
3063  * @cfg {String} smUrl sm image source
3064  * @cfg {String} mdUrl md image source
3065  * @cfg {String} lgUrl lg image source
3066  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067  * 
3068  * @constructor
3069  * Create a new Input
3070  * @param {Object} config The config object
3071  */
3072
3073 Roo.bootstrap.Img = function(config){
3074     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3075     
3076     this.addEvents({
3077         // img events
3078         /**
3079          * @event click
3080          * The img click event for the img.
3081          * @param {Roo.EventObject} e
3082          */
3083         "click" : true,
3084         /**
3085          * @event load
3086          * The when any image loads
3087          * @param {Roo.EventObject} e
3088          */
3089         "load" : true
3090     });
3091 };
3092
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3094     
3095     imgResponsive: true,
3096     border: '',
3097     src: 'about:blank',
3098     href: false,
3099     target: false,
3100     xsUrl: '',
3101     smUrl: '',
3102     mdUrl: '',
3103     lgUrl: '',
3104     backgroundContain : false,
3105
3106     getAutoCreate : function()
3107     {   
3108         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109             return this.createSingleImg();
3110         }
3111         
3112         var cfg = {
3113             tag: 'div',
3114             cls: 'roo-image-responsive-group',
3115             cn: []
3116         };
3117         var _this = this;
3118         
3119         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3120             
3121             if(!_this[size + 'Url']){
3122                 return;
3123             }
3124             
3125             var img = {
3126                 tag: 'img',
3127                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128                 html: _this.html || cfg.html,
3129                 src: _this[size + 'Url']
3130             };
3131             
3132             img.cls += ' roo-image-responsive-' + size;
3133             
3134             var s = ['xs', 'sm', 'md', 'lg'];
3135             
3136             s.splice(s.indexOf(size), 1);
3137             
3138             Roo.each(s, function(ss){
3139                 img.cls += ' hidden-' + ss;
3140             });
3141             
3142             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143                 cfg.cls += ' img-' + _this.border;
3144             }
3145             
3146             if(_this.alt){
3147                 cfg.alt = _this.alt;
3148             }
3149             
3150             if(_this.href){
3151                 var a = {
3152                     tag: 'a',
3153                     href: _this.href,
3154                     cn: [
3155                         img
3156                     ]
3157                 };
3158
3159                 if(this.target){
3160                     a.target = _this.target;
3161                 }
3162             }
3163             
3164             cfg.cn.push((_this.href) ? a : img);
3165             
3166         });
3167         
3168         return cfg;
3169     },
3170     
3171     createSingleImg : function()
3172     {
3173         var cfg = {
3174             tag: 'img',
3175             cls: (this.imgResponsive) ? 'img-responsive' : '',
3176             html : null,
3177             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3178         };
3179         
3180         if (this.backgroundContain) {
3181             cfg.cls += ' background-contain';
3182         }
3183         
3184         cfg.html = this.html || cfg.html;
3185         
3186         if (this.backgroundContain) {
3187             cfg.style="background-image: url(" + this.src + ')';
3188         } else {
3189             cfg.src = this.src || cfg.src;
3190         }
3191         
3192         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193             cfg.cls += ' img-' + this.border;
3194         }
3195         
3196         if(this.alt){
3197             cfg.alt = this.alt;
3198         }
3199         
3200         if(this.href){
3201             var a = {
3202                 tag: 'a',
3203                 href: this.href,
3204                 cn: [
3205                     cfg
3206                 ]
3207             };
3208             
3209             if(this.target){
3210                 a.target = this.target;
3211             }
3212             
3213         }
3214         
3215         return (this.href) ? a : cfg;
3216     },
3217     
3218     initEvents: function() 
3219     {
3220         if(!this.href){
3221             this.el.on('click', this.onClick, this);
3222         }
3223         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224             this.el.on('load', this.onImageLoad, this);
3225         } else {
3226             // not sure if this works.. not tested
3227             this.el.select('img', true).on('load', this.onImageLoad, this);
3228         }
3229         
3230     },
3231     
3232     onClick : function(e)
3233     {
3234         Roo.log('img onclick');
3235         this.fireEvent('click', this, e);
3236     },
3237     onImageLoad: function(e)
3238     {
3239         Roo.log('img load');
3240         this.fireEvent('load', this, e);
3241     },
3242     
3243     /**
3244      * Sets the url of the image - used to update it
3245      * @param {String} url the url of the image
3246      */
3247     
3248     setSrc : function(url)
3249     {
3250         this.src =  url;
3251         
3252         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253             if (this.backgroundContain) {
3254                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3255             } else {
3256                 this.el.dom.src =  url;
3257             }
3258             return;
3259         }
3260         
3261         this.el.select('img', true).first().dom.src =  url;
3262     }
3263     
3264     
3265    
3266 });
3267
3268  /*
3269  * - LGPL
3270  *
3271  * image
3272  * 
3273  */
3274
3275
3276 /**
3277  * @class Roo.bootstrap.Link
3278  * @extends Roo.bootstrap.Component
3279  * @children Roo.bootstrap.Component
3280  * Bootstrap Link Class (eg. '<a href>')
3281  
3282  * @cfg {String} alt image alternative text
3283  * @cfg {String} href a tag href
3284  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285  * @cfg {String} html the content of the link.
3286  * @cfg {String} anchor name for the anchor link
3287  * @cfg {String} fa - favicon
3288
3289  * @cfg {Boolean} preventDefault (true | false) default false
3290
3291  * 
3292  * @constructor
3293  * Create a new Input
3294  * @param {Object} config The config object
3295  */
3296
3297 Roo.bootstrap.Link = function(config){
3298     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3299     
3300     this.addEvents({
3301         // img events
3302         /**
3303          * @event click
3304          * The img click event for the img.
3305          * @param {Roo.EventObject} e
3306          */
3307         "click" : true
3308     });
3309 };
3310
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3312     
3313     href: false,
3314     target: false,
3315     preventDefault: false,
3316     anchor : false,
3317     alt : false,
3318     fa: false,
3319
3320
3321     getAutoCreate : function()
3322     {
3323         var html = this.html || '';
3324         
3325         if (this.fa !== false) {
3326             html = '<i class="fa fa-' + this.fa + '"></i>';
3327         }
3328         var cfg = {
3329             tag: 'a'
3330         };
3331         // anchor's do not require html/href...
3332         if (this.anchor === false) {
3333             cfg.html = html;
3334             cfg.href = this.href || '#';
3335         } else {
3336             cfg.name = this.anchor;
3337             if (this.html !== false || this.fa !== false) {
3338                 cfg.html = html;
3339             }
3340             if (this.href !== false) {
3341                 cfg.href = this.href;
3342             }
3343         }
3344         
3345         if(this.alt !== false){
3346             cfg.alt = this.alt;
3347         }
3348         
3349         
3350         if(this.target !== false) {
3351             cfg.target = this.target;
3352         }
3353         
3354         return cfg;
3355     },
3356     
3357     initEvents: function() {
3358         
3359         if(!this.href || this.preventDefault){
3360             this.el.on('click', this.onClick, this);
3361         }
3362     },
3363     
3364     onClick : function(e)
3365     {
3366         if(this.preventDefault){
3367             e.preventDefault();
3368         }
3369         //Roo.log('img onclick');
3370         this.fireEvent('click', this, e);
3371     }
3372    
3373 });
3374
3375  /*
3376  * - LGPL
3377  *
3378  * header
3379  * 
3380  */
3381
3382 /**
3383  * @class Roo.bootstrap.Header
3384  * @extends Roo.bootstrap.Component
3385  * @children Roo.bootstrap.Component
3386  * Bootstrap Header class
3387  *
3388  * 
3389  * @cfg {String} html content of header
3390  * @cfg {Number} level (1|2|3|4|5|6) default 1
3391  * 
3392  * @constructor
3393  * Create a new Header
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.Header  = function(config){
3399     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3403     
3404     //href : false,
3405     html : false,
3406     level : 1,
3407     
3408     
3409     
3410     getAutoCreate : function(){
3411         
3412         
3413         
3414         var cfg = {
3415             tag: 'h' + (1 *this.level),
3416             html: this.html || ''
3417         } ;
3418         
3419         return cfg;
3420     }
3421    
3422 });
3423
3424  
3425
3426  /**
3427  * @class Roo.bootstrap.MenuMgr
3428  * @licence LGPL
3429  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430  * @static
3431  */
3432 Roo.bootstrap.menu.Manager = function(){
3433    var menus, active, groups = {}, attached = false, lastShow = new Date();
3434
3435    // private - called when first menu is created
3436    function init(){
3437        menus = {};
3438        active = new Roo.util.MixedCollection();
3439        Roo.get(document).addKeyListener(27, function(){
3440            if(active.length > 0){
3441                hideAll();
3442            }
3443        });
3444    }
3445
3446    // private
3447    function hideAll(){
3448        if(active && active.length > 0){
3449            var c = active.clone();
3450            c.each(function(m){
3451                m.hide();
3452            });
3453        }
3454    }
3455
3456    // private
3457    function onHide(m){
3458        active.remove(m);
3459        if(active.length < 1){
3460            Roo.get(document).un("mouseup", onMouseDown);
3461             
3462            attached = false;
3463        }
3464    }
3465
3466    // private
3467    function onShow(m){
3468        var last = active.last();
3469        lastShow = new Date();
3470        active.add(m);
3471        if(!attached){
3472           Roo.get(document).on("mouseup", onMouseDown);
3473            
3474            attached = true;
3475        }
3476        if(m.parentMenu){
3477           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478           m.parentMenu.activeChild = m;
3479        }else if(last && last.isVisible()){
3480           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3481        }
3482    }
3483
3484    // private
3485    function onBeforeHide(m){
3486        if(m.activeChild){
3487            m.activeChild.hide();
3488        }
3489        if(m.autoHideTimer){
3490            clearTimeout(m.autoHideTimer);
3491            delete m.autoHideTimer;
3492        }
3493    }
3494
3495    // private
3496    function onBeforeShow(m){
3497        var pm = m.parentMenu;
3498        if(!pm && !m.allowOtherMenus){
3499            hideAll();
3500        }else if(pm && pm.activeChild && active != m){
3501            pm.activeChild.hide();
3502        }
3503    }
3504
3505    // private this should really trigger on mouseup..
3506    function onMouseDown(e){
3507         Roo.log("on Mouse Up");
3508         
3509         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510             Roo.log("MenuManager hideAll");
3511             hideAll();
3512             e.stopEvent();
3513         }
3514         
3515         
3516    }
3517
3518    // private
3519    function onBeforeCheck(mi, state){
3520        if(state){
3521            var g = groups[mi.group];
3522            for(var i = 0, l = g.length; i < l; i++){
3523                if(g[i] != mi){
3524                    g[i].setChecked(false);
3525                }
3526            }
3527        }
3528    }
3529
3530    return {
3531
3532        /**
3533         * Hides all menus that are currently visible
3534         */
3535        hideAll : function(){
3536             hideAll();  
3537        },
3538
3539        // private
3540        register : function(menu){
3541            if(!menus){
3542                init();
3543            }
3544            menus[menu.id] = menu;
3545            menu.on("beforehide", onBeforeHide);
3546            menu.on("hide", onHide);
3547            menu.on("beforeshow", onBeforeShow);
3548            menu.on("show", onShow);
3549            var g = menu.group;
3550            if(g && menu.events["checkchange"]){
3551                if(!groups[g]){
3552                    groups[g] = [];
3553                }
3554                groups[g].push(menu);
3555                menu.on("checkchange", onCheck);
3556            }
3557        },
3558
3559         /**
3560          * Returns a {@link Roo.menu.Menu} object
3561          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562          * be used to generate and return a new Menu instance.
3563          */
3564        get : function(menu){
3565            if(typeof menu == "string"){ // menu id
3566                return menus[menu];
3567            }else if(menu.events){  // menu instance
3568                return menu;
3569            }
3570            /*else if(typeof menu.length == 'number'){ // array of menu items?
3571                return new Roo.bootstrap.Menu({items:menu});
3572            }else{ // otherwise, must be a config
3573                return new Roo.bootstrap.Menu(menu);
3574            }
3575            */
3576            return false;
3577        },
3578
3579        // private
3580        unregister : function(menu){
3581            delete menus[menu.id];
3582            menu.un("beforehide", onBeforeHide);
3583            menu.un("hide", onHide);
3584            menu.un("beforeshow", onBeforeShow);
3585            menu.un("show", onShow);
3586            var g = menu.group;
3587            if(g && menu.events["checkchange"]){
3588                groups[g].remove(menu);
3589                menu.un("checkchange", onCheck);
3590            }
3591        },
3592
3593        // private
3594        registerCheckable : function(menuItem){
3595            var g = menuItem.group;
3596            if(g){
3597                if(!groups[g]){
3598                    groups[g] = [];
3599                }
3600                groups[g].push(menuItem);
3601                menuItem.on("beforecheckchange", onBeforeCheck);
3602            }
3603        },
3604
3605        // private
3606        unregisterCheckable : function(menuItem){
3607            var g = menuItem.group;
3608            if(g){
3609                groups[g].remove(menuItem);
3610                menuItem.un("beforecheckchange", onBeforeCheck);
3611            }
3612        }
3613    };
3614 }(); 
3615 /**
3616  * @class Roo.bootstrap.menu.Menu
3617  * @extends Roo.bootstrap.Component
3618  * @licence LGPL
3619  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3620  * @parent none
3621  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3622  * 
3623  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3625  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3626  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3628 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3629  
3630  * @constructor
3631  * Create a new Menu
3632  * @param {Object} config The config objectQ
3633  */
3634
3635
3636 Roo.bootstrap.menu.Menu = function(config){
3637     
3638     if (config.type == 'treeview') {
3639         // normally menu's are drawn attached to the document to handle layering etc..
3640         // however treeview (used by the docs menu is drawn into the parent element)
3641         this.container_method = 'getChildContainer'; 
3642     }
3643     
3644     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645     if (this.registerMenu && this.type != 'treeview')  {
3646         Roo.bootstrap.menu.Manager.register(this);
3647     }
3648     
3649     
3650     this.addEvents({
3651         /**
3652          * @event beforeshow
3653          * Fires before this menu is displayed (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforeshow : true,
3657         /**
3658          * @event beforehide
3659          * Fires before this menu is hidden (return false to block)
3660          * @param {Roo.menu.Menu} this
3661          */
3662         beforehide : true,
3663         /**
3664          * @event show
3665          * Fires after this menu is displayed
3666          * @param {Roo.menu.Menu} this
3667          */
3668         show : true,
3669         /**
3670          * @event hide
3671          * Fires after this menu is hidden
3672          * @param {Roo.menu.Menu} this
3673          */
3674         hide : true,
3675         /**
3676          * @event click
3677          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680          * @param {Roo.EventObject} e
3681          */
3682         click : true,
3683         /**
3684          * @event mouseover
3685          * Fires when the mouse is hovering over this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseover : true,
3691         /**
3692          * @event mouseout
3693          * Fires when the mouse exits this menu
3694          * @param {Roo.menu.Menu} this
3695          * @param {Roo.EventObject} e
3696          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3697          */
3698         mouseout : true,
3699         /**
3700          * @event itemclick
3701          * Fires when a menu item contained in this menu is clicked
3702          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703          * @param {Roo.EventObject} e
3704          */
3705         itemclick: true
3706     });
3707     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 };
3709
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3711     
3712    /// html : false,
3713    
3714     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3715     type: false,
3716     /**
3717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718      */
3719     registerMenu : true,
3720     
3721     menuItems :false, // stores the menu items..
3722     
3723     hidden:true,
3724         
3725     parentMenu : false,
3726     
3727     stopEvent : true,
3728     
3729     isLink : false,
3730     
3731     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732     
3733     hideTrigger : false,
3734     
3735     align : 'tl-bl?',
3736     
3737     
3738     getChildContainer : function() {
3739         return this.el;  
3740     },
3741     
3742     getAutoCreate : function(){
3743          
3744         //if (['right'].indexOf(this.align)!==-1) {
3745         //    cfg.cn[1].cls += ' pull-right'
3746         //}
3747          
3748         var cfg = {
3749             tag : 'ul',
3750             cls : 'dropdown-menu shadow' ,
3751             style : 'z-index:1000'
3752             
3753         };
3754         
3755         if (this.type === 'submenu') {
3756             cfg.cls = 'submenu active';
3757         }
3758         if (this.type === 'treeview') {
3759             cfg.cls = 'treeview-menu';
3760         }
3761         
3762         return cfg;
3763     },
3764     initEvents : function() {
3765         
3766        // Roo.log("ADD event");
3767        // Roo.log(this.triggerEl.dom);
3768         if (this.triggerEl) {
3769             
3770             this.triggerEl.on('click', this.onTriggerClick, this);
3771             
3772             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773             
3774             if (!this.hideTrigger) {
3775                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776                     // dropdown toggle on the 'a' in BS4?
3777                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778                 } else {
3779                     this.triggerEl.addClass('dropdown-toggle');
3780                 }
3781             }
3782         }
3783         
3784         if (Roo.isTouch) {
3785             this.el.on('touchstart'  , this.onTouch, this);
3786         }
3787         this.el.on('click' , this.onClick, this);
3788
3789         this.el.on("mouseover", this.onMouseOver, this);
3790         this.el.on("mouseout", this.onMouseOut, this);
3791         
3792     },
3793     
3794     findTargetItem : function(e)
3795     {
3796         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3797         if(!t){
3798             return false;
3799         }
3800         //Roo.log(t);         Roo.log(t.id);
3801         if(t && t.id){
3802             //Roo.log(this.menuitems);
3803             return this.menuitems.get(t.id);
3804             
3805             //return this.items.get(t.menuItemId);
3806         }
3807         
3808         return false;
3809     },
3810     
3811     onTouch : function(e) 
3812     {
3813         Roo.log("menu.onTouch");
3814         //e.stopEvent(); this make the user popdown broken
3815         this.onClick(e);
3816     },
3817     
3818     onClick : function(e)
3819     {
3820         Roo.log("menu.onClick");
3821         
3822         var t = this.findTargetItem(e);
3823         if(!t || t.isContainer){
3824             return;
3825         }
3826         Roo.log(e);
3827         /*
3828         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3829             if(t == this.activeItem && t.shouldDeactivate(e)){
3830                 this.activeItem.deactivate();
3831                 delete this.activeItem;
3832                 return;
3833             }
3834             if(t.canActivate){
3835                 this.setActiveItem(t, true);
3836             }
3837             return;
3838             
3839             
3840         }
3841         */
3842        
3843         Roo.log('pass click event');
3844         
3845         t.onClick(e);
3846         
3847         this.fireEvent("click", this, t, e);
3848         
3849         var _this = this;
3850         
3851         if(!t.href.length || t.href == '#'){
3852             (function() { _this.hide(); }).defer(100);
3853         }
3854         
3855     },
3856     
3857     onMouseOver : function(e){
3858         var t  = this.findTargetItem(e);
3859         //Roo.log(t);
3860         //if(t){
3861         //    if(t.canActivate && !t.disabled){
3862         //        this.setActiveItem(t, true);
3863         //    }
3864         //}
3865         
3866         this.fireEvent("mouseover", this, e, t);
3867     },
3868     isVisible : function(){
3869         return !this.hidden;
3870     },
3871     onMouseOut : function(e){
3872         var t  = this.findTargetItem(e);
3873         
3874         //if(t ){
3875         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3876         //        this.activeItem.deactivate();
3877         //        delete this.activeItem;
3878         //    }
3879         //}
3880         this.fireEvent("mouseout", this, e, t);
3881     },
3882     
3883     
3884     /**
3885      * Displays this menu relative to another element
3886      * @param {String/HTMLElement/Roo.Element} element The element to align to
3887      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888      * the element (defaults to this.defaultAlign)
3889      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890      */
3891     show : function(el, pos, parentMenu)
3892     {
3893         if (false === this.fireEvent("beforeshow", this)) {
3894             Roo.log("show canceled");
3895             return;
3896         }
3897         this.parentMenu = parentMenu;
3898         if(!this.el){
3899             this.render();
3900         }
3901         this.el.addClass('show'); // show otherwise we do not know how big we are..
3902          
3903         var xy = this.el.getAlignToXY(el, pos);
3904         
3905         // bl-tl << left align  below
3906         // tl-bl << left align 
3907         
3908         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909             // if it goes to far to the right.. -> align left.
3910             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911         }
3912         if(xy[0] < 0){
3913             // was left align - go right?
3914             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915         }
3916         
3917         // goes down the bottom
3918         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919            xy[1]  < 0 ){
3920             var a = this.align.replace('?', '').split('-');
3921             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3922             
3923         }
3924         
3925         this.showAt(  xy , parentMenu, false);
3926     },
3927      /**
3928      * Displays this menu at a specific xy position
3929      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931      */
3932     showAt : function(xy, parentMenu, /* private: */_e){
3933         this.parentMenu = parentMenu;
3934         if(!this.el){
3935             this.render();
3936         }
3937         if(_e !== false){
3938             this.fireEvent("beforeshow", this);
3939             //xy = this.el.adjustForConstraints(xy);
3940         }
3941         
3942         //this.el.show();
3943         this.hideMenuItems();
3944         this.hidden = false;
3945         if (this.triggerEl) {
3946             this.triggerEl.addClass('open');
3947         }
3948         
3949         this.el.addClass('show');
3950         
3951         
3952         
3953         // reassign x when hitting right
3954         
3955         // reassign y when hitting bottom
3956         
3957         // but the list may align on trigger left or trigger top... should it be a properity?
3958         
3959         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3960             this.el.setXY(xy);
3961         }
3962         
3963         this.focus();
3964         this.fireEvent("show", this);
3965     },
3966     
3967     focus : function(){
3968         return;
3969         if(!this.hidden){
3970             this.doFocus.defer(50, this);
3971         }
3972     },
3973
3974     doFocus : function(){
3975         if(!this.hidden){
3976             this.focusEl.focus();
3977         }
3978     },
3979
3980     /**
3981      * Hides this menu and optionally all parent menus
3982      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983      */
3984     hide : function(deep)
3985     {
3986         if (false === this.fireEvent("beforehide", this)) {
3987             Roo.log("hide canceled");
3988             return;
3989         }
3990         this.hideMenuItems();
3991         if(this.el && this.isVisible()){
3992            
3993             if(this.activeItem){
3994                 this.activeItem.deactivate();
3995                 this.activeItem = null;
3996             }
3997             if (this.triggerEl) {
3998                 this.triggerEl.removeClass('open');
3999             }
4000             
4001             this.el.removeClass('show');
4002             this.hidden = true;
4003             this.fireEvent("hide", this);
4004         }
4005         if(deep === true && this.parentMenu){
4006             this.parentMenu.hide(true);
4007         }
4008     },
4009     
4010     onTriggerClick : function(e)
4011     {
4012         Roo.log('trigger click');
4013         
4014         var target = e.getTarget();
4015         
4016         Roo.log(target.nodeName.toLowerCase());
4017         
4018         if(target.nodeName.toLowerCase() === 'i'){
4019             e.preventDefault();
4020         }
4021         
4022     },
4023     
4024     onTriggerPress  : function(e)
4025     {
4026         Roo.log('trigger press');
4027         //Roo.log(e.getTarget());
4028        // Roo.log(this.triggerEl.dom);
4029        
4030         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031         var pel = Roo.get(e.getTarget());
4032         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033             Roo.log('is treeview or dropdown?');
4034             return;
4035         }
4036         
4037         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038             return;
4039         }
4040         
4041         if (this.isVisible()) {
4042             Roo.log('hide');
4043             this.hide();
4044         } else {
4045             Roo.log('show');
4046             
4047             this.show(this.triggerEl, this.align, false);
4048         }
4049         
4050         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4051             e.stopEvent();
4052         }
4053         
4054     },
4055        
4056     
4057     hideMenuItems : function()
4058     {
4059         Roo.log("hide Menu Items");
4060         if (!this.el) { 
4061             return;
4062         }
4063         
4064         this.el.select('.open',true).each(function(aa) {
4065             
4066             aa.removeClass('open');
4067          
4068         });
4069     },
4070     addxtypeChild : function (tree, cntr) {
4071         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072           
4073         this.menuitems.add(comp);
4074         return comp;
4075
4076     },
4077     getEl : function()
4078     {
4079         Roo.log(this.el);
4080         return this.el;
4081     },
4082     
4083     clear : function()
4084     {
4085         this.getEl().dom.innerHTML = '';
4086         this.menuitems.clear();
4087     }
4088 });
4089
4090  
4091  /**
4092  * @class Roo.bootstrap.menu.Item
4093  * @extends Roo.bootstrap.Component
4094  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095  * @parent Roo.bootstrap.menu.Menu
4096  * @licence LGPL
4097  * Bootstrap MenuItem class
4098  * 
4099  * @cfg {String} html the menu label
4100  * @cfg {String} href the link
4101  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4104  * @cfg {String} fa favicon to show on left of menu item.
4105  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4106  * 
4107  * 
4108  * @constructor
4109  * Create a new MenuItem
4110  * @param {Object} config The config object
4111  */
4112
4113
4114 Roo.bootstrap.menu.Item = function(config){
4115     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4116     this.addEvents({
4117         // raw events
4118         /**
4119          * @event click
4120          * The raw click event for the entire grid.
4121          * @param {Roo.bootstrap.menu.Item} this
4122          * @param {Roo.EventObject} e
4123          */
4124         "click" : true
4125     });
4126 };
4127
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4129     
4130     href : false,
4131     html : false,
4132     preventDefault: false,
4133     isContainer : false,
4134     active : false,
4135     fa: false,
4136     
4137     getAutoCreate : function(){
4138         
4139         if(this.isContainer){
4140             return {
4141                 tag: 'li',
4142                 cls: 'dropdown-menu-item '
4143             };
4144         }
4145         var ctag = {
4146             tag: 'span',
4147             html: 'Link'
4148         };
4149         
4150         var anc = {
4151             tag : 'a',
4152             cls : 'dropdown-item',
4153             href : '#',
4154             cn : [  ]
4155         };
4156         
4157         if (this.fa !== false) {
4158             anc.cn.push({
4159                 tag : 'i',
4160                 cls : 'fa fa-' + this.fa
4161             });
4162         }
4163         
4164         anc.cn.push(ctag);
4165         
4166         
4167         var cfg= {
4168             tag: 'li',
4169             cls: 'dropdown-menu-item',
4170             cn: [ anc ]
4171         };
4172         if (this.parent().type == 'treeview') {
4173             cfg.cls = 'treeview-menu';
4174         }
4175         if (this.active) {
4176             cfg.cls += ' active';
4177         }
4178         
4179         
4180         
4181         anc.href = this.href || cfg.cn[0].href ;
4182         ctag.html = this.html || cfg.cn[0].html ;
4183         return cfg;
4184     },
4185     
4186     initEvents: function()
4187     {
4188         if (this.parent().type == 'treeview') {
4189             this.el.select('a').on('click', this.onClick, this);
4190         }
4191         
4192         if (this.menu) {
4193             this.menu.parentType = this.xtype;
4194             this.menu.triggerEl = this.el;
4195             this.menu = this.addxtype(Roo.apply({}, this.menu));
4196         }
4197         
4198     },
4199     onClick : function(e)
4200     {
4201         //Roo.log('item on click ');
4202         
4203         if(this.href === false || 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 === false ||
6547                 this.href === '#' 
6548         ){
6549             //Roo.log("NavItem - prevent Default?");
6550             e.preventDefault();
6551         }
6552         
6553         if (this.disabled) {
6554             return;
6555         }
6556         
6557         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6558         if (tg && tg.transition) {
6559             Roo.log("waiting for the transitionend");
6560             return;
6561         }
6562         
6563         
6564         
6565         //Roo.log("fire event clicked");
6566         if(this.fireEvent('click', this, e) === false){
6567             return;
6568         };
6569         
6570         if(this.tagtype == 'span'){
6571             return;
6572         }
6573         
6574         //Roo.log(this.href);
6575         var ael = this.el.select('a',true).first();
6576         //Roo.log(ael);
6577         
6578         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6579             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6580             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6581                 return; // ignore... - it's a 'hash' to another page.
6582             }
6583             Roo.log("NavItem - prevent Default?");
6584             e.preventDefault();
6585             this.scrollToElement(e);
6586         }
6587         
6588         
6589         var p =  this.parent();
6590    
6591         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6592             if (typeof(p.setActiveItem) !== 'undefined') {
6593                 p.setActiveItem(this);
6594             }
6595         }
6596         
6597         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6598         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6599             // remove the collapsed menu expand...
6600             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6601         }
6602     },
6603     
6604     isActive: function () {
6605         return this.active
6606     },
6607     setActive : function(state, fire, is_was_active)
6608     {
6609         if (this.active && !state && this.navId) {
6610             this.was_active = true;
6611             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6612             if (nv) {
6613                 nv.clearWasActive(this);
6614             }
6615             
6616         }
6617         this.active = state;
6618         
6619         if (!state ) {
6620             this.el.removeClass('active');
6621             this.navLink ? this.navLink.removeClass('active') : false;
6622         } else if (!this.el.hasClass('active')) {
6623             
6624             this.el.addClass('active');
6625             if (Roo.bootstrap.version == 4 && this.navLink ) {
6626                 this.navLink.addClass('active');
6627             }
6628             
6629         }
6630         if (fire) {
6631             this.fireEvent('changed', this, state);
6632         }
6633         
6634         // show a panel if it's registered and related..
6635         
6636         if (!this.navId || !this.tabId || !state || is_was_active) {
6637             return;
6638         }
6639         
6640         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6641         if (!tg) {
6642             return;
6643         }
6644         var pan = tg.getPanelByName(this.tabId);
6645         if (!pan) {
6646             return;
6647         }
6648         // if we can not flip to new panel - go back to old nav highlight..
6649         if (false == tg.showPanel(pan)) {
6650             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6651             if (nv) {
6652                 var onav = nv.getWasActive();
6653                 if (onav) {
6654                     onav.setActive(true, false, true);
6655                 }
6656             }
6657             
6658         }
6659         
6660         
6661         
6662     },
6663      // this should not be here...
6664     setDisabled : function(state)
6665     {
6666         this.disabled = state;
6667         if (!state ) {
6668             this.el.removeClass('disabled');
6669         } else if (!this.el.hasClass('disabled')) {
6670             this.el.addClass('disabled');
6671         }
6672         
6673     },
6674     
6675     /**
6676      * Fetch the element to display the tooltip on.
6677      * @return {Roo.Element} defaults to this.el
6678      */
6679     tooltipEl : function()
6680     {
6681         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6682     },
6683     
6684     scrollToElement : function(e)
6685     {
6686         var c = document.body;
6687         
6688         /*
6689          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6690          */
6691         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6692             c = document.documentElement;
6693         }
6694         
6695         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6696         
6697         if(!target){
6698             return;
6699         }
6700
6701         var o = target.calcOffsetsTo(c);
6702         
6703         var options = {
6704             target : target,
6705             value : o[1]
6706         };
6707         
6708         this.fireEvent('scrollto', this, options, e);
6709         
6710         Roo.get(c).scrollTo('top', options.value, true);
6711         
6712         return;
6713     },
6714     /**
6715      * Set the HTML (text content) of the item
6716      * @param {string} html  content for the nav item
6717      */
6718     setHtml : function(html)
6719     {
6720         this.html = html;
6721         this.htmlEl.dom.innerHTML = html;
6722         
6723     } 
6724 });
6725  
6726
6727  /*
6728  * - LGPL
6729  *
6730  * sidebar item
6731  *
6732  *  li
6733  *    <span> icon </span>
6734  *    <span> text </span>
6735  *    <span>badge </span>
6736  */
6737
6738 /**
6739  * @class Roo.bootstrap.nav.SidebarItem
6740  * @extends Roo.bootstrap.nav.Item
6741  * Bootstrap Navbar.NavSidebarItem class
6742  * 
6743  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6744  * {Boolean} open is the menu open
6745  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6746  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6747  * {String} buttonSize (sm|md|lg)the extra classes for the button
6748  * {Boolean} showArrow show arrow next to the text (default true)
6749  * @constructor
6750  * Create a new Navbar Button
6751  * @param {Object} config The config object
6752  */
6753 Roo.bootstrap.nav.SidebarItem = function(config){
6754     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6755     this.addEvents({
6756         // raw events
6757         /**
6758          * @event click
6759          * The raw click event for the entire grid.
6760          * @param {Roo.EventObject} e
6761          */
6762         "click" : true,
6763          /**
6764             * @event changed
6765             * Fires when the active item active state changes
6766             * @param {Roo.bootstrap.nav.SidebarItem} this
6767             * @param {boolean} state the new state
6768              
6769          */
6770         'changed': true
6771     });
6772    
6773 };
6774
6775 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6776     
6777     badgeWeight : 'default',
6778     
6779     open: false,
6780     
6781     buttonView : false,
6782     
6783     buttonWeight : 'default',
6784     
6785     buttonSize : 'md',
6786     
6787     showArrow : true,
6788     
6789     getAutoCreate : function(){
6790         
6791         
6792         var a = {
6793                 tag: 'a',
6794                 href : this.href || '#',
6795                 cls: '',
6796                 html : '',
6797                 cn : []
6798         };
6799         
6800         if(this.buttonView){
6801             a = {
6802                 tag: 'button',
6803                 href : this.href || '#',
6804                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6805                 html : this.html,
6806                 cn : []
6807             };
6808         }
6809         
6810         var cfg = {
6811             tag: 'li',
6812             cls: '',
6813             cn: [ a ]
6814         };
6815         
6816         if (this.active) {
6817             cfg.cls += ' active';
6818         }
6819         
6820         if (this.disabled) {
6821             cfg.cls += ' disabled';
6822         }
6823         if (this.open) {
6824             cfg.cls += ' open x-open';
6825         }
6826         // left icon..
6827         if (this.glyphicon || this.icon) {
6828             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6829             a.cn.push({ tag : 'i', cls : c }) ;
6830         }
6831         
6832         if(!this.buttonView){
6833             var span = {
6834                 tag: 'span',
6835                 html : this.html || ''
6836             };
6837
6838             a.cn.push(span);
6839             
6840         }
6841         
6842         if (this.badge !== '') {
6843             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6844         }
6845         
6846         if (this.menu) {
6847             
6848             if(this.showArrow){
6849                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6850             }
6851             
6852             a.cls += ' dropdown-toggle treeview' ;
6853         }
6854         
6855         return cfg;
6856     },
6857     
6858     initEvents : function()
6859     { 
6860         if (typeof (this.menu) != 'undefined') {
6861             this.menu.parentType = this.xtype;
6862             this.menu.triggerEl = this.el;
6863             this.menu = this.addxtype(Roo.apply({}, this.menu));
6864         }
6865         
6866         this.el.on('click', this.onClick, this);
6867         
6868         if(this.badge !== ''){
6869             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6870         }
6871         
6872     },
6873     
6874     onClick : function(e)
6875     {
6876         if(this.disabled){
6877             e.preventDefault();
6878             return;
6879         }
6880         
6881         if(this.preventDefault){
6882             e.preventDefault();
6883         }
6884         
6885         this.fireEvent('click', this, e);
6886     },
6887     
6888     disable : function()
6889     {
6890         this.setDisabled(true);
6891     },
6892     
6893     enable : function()
6894     {
6895         this.setDisabled(false);
6896     },
6897     
6898     setDisabled : function(state)
6899     {
6900         if(this.disabled == state){
6901             return;
6902         }
6903         
6904         this.disabled = state;
6905         
6906         if (state) {
6907             this.el.addClass('disabled');
6908             return;
6909         }
6910         
6911         this.el.removeClass('disabled');
6912         
6913         return;
6914     },
6915     
6916     setActive : function(state)
6917     {
6918         if(this.active == state){
6919             return;
6920         }
6921         
6922         this.active = state;
6923         
6924         if (state) {
6925             this.el.addClass('active');
6926             return;
6927         }
6928         
6929         this.el.removeClass('active');
6930         
6931         return;
6932     },
6933     
6934     isActive: function () 
6935     {
6936         return this.active;
6937     },
6938     
6939     setBadge : function(str)
6940     {
6941         if(!this.badgeEl){
6942             return;
6943         }
6944         
6945         this.badgeEl.dom.innerHTML = str;
6946     }
6947     
6948    
6949      
6950  
6951 });
6952  
6953
6954  /*
6955  * - LGPL
6956  *
6957  * nav progress bar
6958  * 
6959  */
6960
6961 /**
6962  * @class Roo.bootstrap.nav.ProgressBar
6963  * @extends Roo.bootstrap.Component
6964  * @children Roo.bootstrap.nav.ProgressBarItem
6965  * Bootstrap NavProgressBar class
6966  * 
6967  * @constructor
6968  * Create a new nav progress bar - a bar indicating step along a process
6969  * @param {Object} config The config object
6970  */
6971
6972 Roo.bootstrap.nav.ProgressBar = function(config){
6973     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6974
6975     this.bullets = this.bullets || [];
6976    
6977 //    Roo.bootstrap.nav.ProgressBar.register(this);
6978      this.addEvents({
6979         /**
6980              * @event changed
6981              * Fires when the active item changes
6982              * @param {Roo.bootstrap.nav.ProgressBar} this
6983              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6984              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6985          */
6986         'changed': true
6987      });
6988     
6989 };
6990
6991 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6992     /**
6993      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6994      * Bullets for the Nav Progress bar for the toolbar
6995      */
6996     bullets : [],
6997     barItems : [],
6998     
6999     getAutoCreate : function()
7000     {
7001         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7002         
7003         cfg = {
7004             tag : 'div',
7005             cls : 'roo-navigation-bar-group',
7006             cn : [
7007                 {
7008                     tag : 'div',
7009                     cls : 'roo-navigation-top-bar'
7010                 },
7011                 {
7012                     tag : 'div',
7013                     cls : 'roo-navigation-bullets-bar',
7014                     cn : [
7015                         {
7016                             tag : 'ul',
7017                             cls : 'roo-navigation-bar'
7018                         }
7019                     ]
7020                 },
7021                 
7022                 {
7023                     tag : 'div',
7024                     cls : 'roo-navigation-bottom-bar'
7025                 }
7026             ]
7027             
7028         };
7029         
7030         return cfg;
7031         
7032     },
7033     
7034     initEvents: function() 
7035     {
7036         
7037     },
7038     
7039     onRender : function(ct, position) 
7040     {
7041         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7042         
7043         if(this.bullets.length){
7044             Roo.each(this.bullets, function(b){
7045                this.addItem(b);
7046             }, this);
7047         }
7048         
7049         this.format();
7050         
7051     },
7052     
7053     addItem : function(cfg)
7054     {
7055         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7056         
7057         item.parentId = this.id;
7058         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7059         
7060         if(cfg.html){
7061             var top = new Roo.bootstrap.Element({
7062                 tag : 'div',
7063                 cls : 'roo-navigation-bar-text'
7064             });
7065             
7066             var bottom = new Roo.bootstrap.Element({
7067                 tag : 'div',
7068                 cls : 'roo-navigation-bar-text'
7069             });
7070             
7071             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7072             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7073             
7074             var topText = new Roo.bootstrap.Element({
7075                 tag : 'span',
7076                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7077             });
7078             
7079             var bottomText = new Roo.bootstrap.Element({
7080                 tag : 'span',
7081                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7082             });
7083             
7084             topText.onRender(top.el, null);
7085             bottomText.onRender(bottom.el, null);
7086             
7087             item.topEl = top;
7088             item.bottomEl = bottom;
7089         }
7090         
7091         this.barItems.push(item);
7092         
7093         return item;
7094     },
7095     
7096     getActive : function()
7097     {
7098         var active = false;
7099         
7100         Roo.each(this.barItems, function(v){
7101             
7102             if (!v.isActive()) {
7103                 return;
7104             }
7105             
7106             active = v;
7107             return false;
7108             
7109         });
7110         
7111         return active;
7112     },
7113     
7114     setActiveItem : function(item)
7115     {
7116         var prev = false;
7117         
7118         Roo.each(this.barItems, function(v){
7119             if (v.rid == item.rid) {
7120                 return ;
7121             }
7122             
7123             if (v.isActive()) {
7124                 v.setActive(false);
7125                 prev = v;
7126             }
7127         });
7128
7129         item.setActive(true);
7130         
7131         this.fireEvent('changed', this, item, prev);
7132     },
7133     
7134     getBarItem: function(rid)
7135     {
7136         var ret = false;
7137         
7138         Roo.each(this.barItems, function(e) {
7139             if (e.rid != rid) {
7140                 return;
7141             }
7142             
7143             ret =  e;
7144             return false;
7145         });
7146         
7147         return ret;
7148     },
7149     
7150     indexOfItem : function(item)
7151     {
7152         var index = false;
7153         
7154         Roo.each(this.barItems, function(v, i){
7155             
7156             if (v.rid != item.rid) {
7157                 return;
7158             }
7159             
7160             index = i;
7161             return false
7162         });
7163         
7164         return index;
7165     },
7166     
7167     setActiveNext : function()
7168     {
7169         var i = this.indexOfItem(this.getActive());
7170         
7171         if (i > this.barItems.length) {
7172             return;
7173         }
7174         
7175         this.setActiveItem(this.barItems[i+1]);
7176     },
7177     
7178     setActivePrev : function()
7179     {
7180         var i = this.indexOfItem(this.getActive());
7181         
7182         if (i  < 1) {
7183             return;
7184         }
7185         
7186         this.setActiveItem(this.barItems[i-1]);
7187     },
7188     
7189     format : function()
7190     {
7191         if(!this.barItems.length){
7192             return;
7193         }
7194      
7195         var width = 100 / this.barItems.length;
7196         
7197         Roo.each(this.barItems, function(i){
7198             i.el.setStyle('width', width + '%');
7199             i.topEl.el.setStyle('width', width + '%');
7200             i.bottomEl.el.setStyle('width', width + '%');
7201         }, this);
7202         
7203     }
7204     
7205 });
7206 /*
7207  * - LGPL
7208  *
7209  * Nav Progress Item
7210  * 
7211  */
7212
7213 /**
7214  * @class Roo.bootstrap.nav.ProgressBarItem
7215  * @extends Roo.bootstrap.Component
7216  * Bootstrap NavProgressBarItem class
7217  * @cfg {String} rid the reference id
7218  * @cfg {Boolean} active (true|false) Is item active default false
7219  * @cfg {Boolean} disabled (true|false) Is item active default false
7220  * @cfg {String} html
7221  * @cfg {String} position (top|bottom) text position default bottom
7222  * @cfg {String} icon show icon instead of number
7223  * 
7224  * @constructor
7225  * Create a new NavProgressBarItem
7226  * @param {Object} config The config object
7227  */
7228 Roo.bootstrap.nav.ProgressBarItem = function(config){
7229     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7230     this.addEvents({
7231         // raw events
7232         /**
7233          * @event click
7234          * The raw click event for the entire grid.
7235          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7236          * @param {Roo.EventObject} e
7237          */
7238         "click" : true
7239     });
7240    
7241 };
7242
7243 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7244     
7245     rid : '',
7246     active : false,
7247     disabled : false,
7248     html : '',
7249     position : 'bottom',
7250     icon : false,
7251     
7252     getAutoCreate : function()
7253     {
7254         var iconCls = 'roo-navigation-bar-item-icon';
7255         
7256         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7257         
7258         var cfg = {
7259             tag: 'li',
7260             cls: 'roo-navigation-bar-item',
7261             cn : [
7262                 {
7263                     tag : 'i',
7264                     cls : iconCls
7265                 }
7266             ]
7267         };
7268         
7269         if(this.active){
7270             cfg.cls += ' active';
7271         }
7272         if(this.disabled){
7273             cfg.cls += ' disabled';
7274         }
7275         
7276         return cfg;
7277     },
7278     
7279     disable : function()
7280     {
7281         this.setDisabled(true);
7282     },
7283     
7284     enable : function()
7285     {
7286         this.setDisabled(false);
7287     },
7288     
7289     initEvents: function() 
7290     {
7291         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7292         
7293         this.iconEl.on('click', this.onClick, this);
7294     },
7295     
7296     onClick : function(e)
7297     {
7298         e.preventDefault();
7299         
7300         if(this.disabled){
7301             return;
7302         }
7303         
7304         if(this.fireEvent('click', this, e) === false){
7305             return;
7306         };
7307         
7308         this.parent().setActiveItem(this);
7309     },
7310     
7311     isActive: function () 
7312     {
7313         return this.active;
7314     },
7315     
7316     setActive : function(state)
7317     {
7318         if(this.active == state){
7319             return;
7320         }
7321         
7322         this.active = state;
7323         
7324         if (state) {
7325             this.el.addClass('active');
7326             return;
7327         }
7328         
7329         this.el.removeClass('active');
7330         
7331         return;
7332     },
7333     
7334     setDisabled : function(state)
7335     {
7336         if(this.disabled == state){
7337             return;
7338         }
7339         
7340         this.disabled = state;
7341         
7342         if (state) {
7343             this.el.addClass('disabled');
7344             return;
7345         }
7346         
7347         this.el.removeClass('disabled');
7348     },
7349     
7350     tooltipEl : function()
7351     {
7352         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7353     }
7354 });
7355  
7356
7357  /*
7358  * - LGPL
7359  *
7360  *  Breadcrumb Nav
7361  * 
7362  */
7363 Roo.namespace('Roo.bootstrap.breadcrumb');
7364
7365
7366 /**
7367  * @class Roo.bootstrap.breadcrumb.Nav
7368  * @extends Roo.bootstrap.Component
7369  * Bootstrap Breadcrumb Nav Class
7370  *  
7371  * @children Roo.bootstrap.breadcrumb.Item
7372  * 
7373  * @constructor
7374  * Create a new breadcrumb.Nav
7375  * @param {Object} config The config object
7376  */
7377
7378
7379 Roo.bootstrap.breadcrumb.Nav = function(config){
7380     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7381     
7382     
7383 };
7384
7385 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7386     
7387     getAutoCreate : function()
7388     {
7389
7390         var cfg = {
7391             tag: 'nav',
7392             cn : [
7393                 {
7394                     tag : 'ol',
7395                     cls : 'breadcrumb'
7396                 }
7397             ]
7398             
7399         };
7400           
7401         return cfg;
7402     },
7403     
7404     initEvents: function()
7405     {
7406         this.olEl = this.el.select('ol',true).first();    
7407     },
7408     getChildContainer : function()
7409     {
7410         return this.olEl;  
7411     }
7412     
7413 });
7414
7415  /*
7416  * - LGPL
7417  *
7418  *  Breadcrumb Item
7419  * 
7420  */
7421
7422
7423 /**
7424  * @class Roo.bootstrap.breadcrumb.Nav
7425  * @extends Roo.bootstrap.Component
7426  * @children Roo.bootstrap.Component
7427  * @parent Roo.bootstrap.breadcrumb.Nav
7428  * Bootstrap Breadcrumb Nav Class
7429  *  
7430  * 
7431  * @cfg {String} html the content of the link.
7432  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7433  * @cfg {Boolean} active is it active
7434
7435  * 
7436  * @constructor
7437  * Create a new breadcrumb.Nav
7438  * @param {Object} config The config object
7439  */
7440
7441 Roo.bootstrap.breadcrumb.Item = function(config){
7442     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7443     this.addEvents({
7444         // img events
7445         /**
7446          * @event click
7447          * The img click event for the img.
7448          * @param {Roo.EventObject} e
7449          */
7450         "click" : true
7451     });
7452     
7453 };
7454
7455 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7456     
7457     href: false,
7458     html : '',
7459     
7460     getAutoCreate : function()
7461     {
7462
7463         var cfg = {
7464             tag: 'li',
7465             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7466         };
7467         if (this.href !== false) {
7468             cfg.cn = [{
7469                 tag : 'a',
7470                 href : this.href,
7471                 html : this.html
7472             }];
7473         } else {
7474             cfg.html = this.html;
7475         }
7476         
7477         return cfg;
7478     },
7479     
7480     initEvents: function()
7481     {
7482         if (this.href) {
7483             this.el.select('a', true).first().on('click',this.onClick, this)
7484         }
7485         
7486     },
7487     onClick : function(e)
7488     {
7489         e.preventDefault();
7490         this.fireEvent('click',this,  e);
7491     }
7492     
7493 });
7494
7495  /*
7496  * - LGPL
7497  *
7498  * row
7499  * 
7500  */
7501
7502 /**
7503  * @class Roo.bootstrap.Row
7504  * @extends Roo.bootstrap.Component
7505  * @children Roo.bootstrap.Component
7506  * Bootstrap Row class (contains columns...)
7507  * 
7508  * @constructor
7509  * Create a new Row
7510  * @param {Object} config The config object
7511  */
7512
7513 Roo.bootstrap.Row = function(config){
7514     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7515 };
7516
7517 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7518     
7519     getAutoCreate : function(){
7520        return {
7521             cls: 'row clearfix'
7522        };
7523     }
7524     
7525     
7526 });
7527
7528  
7529
7530  /*
7531  * - LGPL
7532  *
7533  * pagination
7534  * 
7535  */
7536
7537 /**
7538  * @class Roo.bootstrap.Pagination
7539  * @extends Roo.bootstrap.Component
7540  * @children Roo.bootstrap.Pagination
7541  * Bootstrap Pagination class
7542  * 
7543  * @cfg {String} size (xs|sm|md|lg|xl)
7544  * @cfg {Boolean} inverse 
7545  * 
7546  * @constructor
7547  * Create a new Pagination
7548  * @param {Object} config The config object
7549  */
7550
7551 Roo.bootstrap.Pagination = function(config){
7552     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7553 };
7554
7555 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7556     
7557     cls: false,
7558     size: false,
7559     inverse: false,
7560     
7561     getAutoCreate : function(){
7562         var cfg = {
7563             tag: 'ul',
7564                 cls: 'pagination'
7565         };
7566         if (this.inverse) {
7567             cfg.cls += ' inverse';
7568         }
7569         if (this.html) {
7570             cfg.html=this.html;
7571         }
7572         if (this.cls) {
7573             cfg.cls += " " + this.cls;
7574         }
7575         return cfg;
7576     }
7577    
7578 });
7579
7580  
7581
7582  /*
7583  * - LGPL
7584  *
7585  * Pagination item
7586  * 
7587  */
7588
7589
7590 /**
7591  * @class Roo.bootstrap.PaginationItem
7592  * @extends Roo.bootstrap.Component
7593  * Bootstrap PaginationItem class
7594  * @cfg {String} html text
7595  * @cfg {String} href the link
7596  * @cfg {Boolean} preventDefault (true | false) default true
7597  * @cfg {Boolean} active (true | false) default false
7598  * @cfg {Boolean} disabled default false
7599  * 
7600  * 
7601  * @constructor
7602  * Create a new PaginationItem
7603  * @param {Object} config The config object
7604  */
7605
7606
7607 Roo.bootstrap.PaginationItem = function(config){
7608     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7609     this.addEvents({
7610         // raw events
7611         /**
7612          * @event click
7613          * The raw click event for the entire grid.
7614          * @param {Roo.EventObject} e
7615          */
7616         "click" : true
7617     });
7618 };
7619
7620 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7621     
7622     href : false,
7623     html : false,
7624     preventDefault: true,
7625     active : false,
7626     cls : false,
7627     disabled: false,
7628     
7629     getAutoCreate : function(){
7630         var cfg= {
7631             tag: 'li',
7632             cn: [
7633                 {
7634                     tag : 'a',
7635                     href : this.href ? this.href : '#',
7636                     html : this.html ? this.html : ''
7637                 }
7638             ]
7639         };
7640         
7641         if(this.cls){
7642             cfg.cls = this.cls;
7643         }
7644         
7645         if(this.disabled){
7646             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7647         }
7648         
7649         if(this.active){
7650             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7651         }
7652         
7653         return cfg;
7654     },
7655     
7656     initEvents: function() {
7657         
7658         this.el.on('click', this.onClick, this);
7659         
7660     },
7661     onClick : function(e)
7662     {
7663         Roo.log('PaginationItem on click ');
7664         if(this.preventDefault){
7665             e.preventDefault();
7666         }
7667         
7668         if(this.disabled){
7669             return;
7670         }
7671         
7672         this.fireEvent('click', this, e);
7673     }
7674    
7675 });
7676
7677  
7678
7679  /*
7680  * - LGPL
7681  *
7682  * slider
7683  * 
7684  */
7685
7686
7687 /**
7688  * @class Roo.bootstrap.Slider
7689  * @extends Roo.bootstrap.Component
7690  * Bootstrap Slider class
7691  *    
7692  * @constructor
7693  * Create a new Slider
7694  * @param {Object} config The config object
7695  */
7696
7697 Roo.bootstrap.Slider = function(config){
7698     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7699 };
7700
7701 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7702     
7703     getAutoCreate : function(){
7704         
7705         var cfg = {
7706             tag: 'div',
7707             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7708             cn: [
7709                 {
7710                     tag: 'a',
7711                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7712                 }
7713             ]
7714         };
7715         
7716         return cfg;
7717     }
7718    
7719 });
7720
7721  /*
7722  * Based on:
7723  * Ext JS Library 1.1.1
7724  * Copyright(c) 2006-2007, Ext JS, LLC.
7725  *
7726  * Originally Released Under LGPL - original licence link has changed is not relivant.
7727  *
7728  * Fork - LGPL
7729  * <script type="text/javascript">
7730  */
7731  /**
7732  * @extends Roo.dd.DDProxy
7733  * @class Roo.grid.SplitDragZone
7734  * Support for Column Header resizing
7735  * @constructor
7736  * @param {Object} config
7737  */
7738 // private
7739 // This is a support class used internally by the Grid components
7740 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7741     this.grid = grid;
7742     this.view = grid.getView();
7743     this.proxy = this.view.resizeProxy;
7744     Roo.grid.SplitDragZone.superclass.constructor.call(
7745         this,
7746         hd, // ID
7747         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7748         {  // CONFIG
7749             dragElId : Roo.id(this.proxy.dom),
7750             resizeFrame:false
7751         }
7752     );
7753     
7754     this.setHandleElId(Roo.id(hd));
7755     if (hd2 !== false) {
7756         this.setOuterHandleElId(Roo.id(hd2));
7757     }
7758     
7759     this.scroll = false;
7760 };
7761 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7762     fly: Roo.Element.fly,
7763
7764     b4StartDrag : function(x, y){
7765         this.view.headersDisabled = true;
7766         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7767                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7768         );
7769         this.proxy.setHeight(h);
7770         
7771         // for old system colWidth really stored the actual width?
7772         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7773         // which in reality did not work.. - it worked only for fixed sizes
7774         // for resizable we need to use actual sizes.
7775         var w = this.cm.getColumnWidth(this.cellIndex);
7776         if (!this.view.mainWrap) {
7777             // bootstrap.
7778             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7779         }
7780         
7781         
7782         
7783         // this was w-this.grid.minColumnWidth;
7784         // doesnt really make sense? - w = thie curren width or the rendered one?
7785         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7786         this.resetConstraints();
7787         this.setXConstraint(minw, 1000);
7788         this.setYConstraint(0, 0);
7789         this.minX = x - minw;
7790         this.maxX = x + 1000;
7791         this.startPos = x;
7792         if (!this.view.mainWrap) { // this is Bootstrap code..
7793             this.getDragEl().style.display='block';
7794         }
7795         
7796         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7797     },
7798
7799
7800     handleMouseDown : function(e){
7801         ev = Roo.EventObject.setEvent(e);
7802         var t = this.fly(ev.getTarget());
7803         if(t.hasClass("x-grid-split")){
7804             this.cellIndex = this.view.getCellIndex(t.dom);
7805             this.split = t.dom;
7806             this.cm = this.grid.colModel;
7807             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7808                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7809             }
7810         }
7811     },
7812
7813     endDrag : function(e){
7814         this.view.headersDisabled = false;
7815         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7816         var diff = endX - this.startPos;
7817         // 
7818         var w = this.cm.getColumnWidth(this.cellIndex);
7819         if (!this.view.mainWrap) {
7820             w = 0;
7821         }
7822         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7823     },
7824
7825     autoOffset : function(){
7826         this.setDelta(0,0);
7827     }
7828 });/*
7829  * Based on:
7830  * Ext JS Library 1.1.1
7831  * Copyright(c) 2006-2007, Ext JS, LLC.
7832  *
7833  * Originally Released Under LGPL - original licence link has changed is not relivant.
7834  *
7835  * Fork - LGPL
7836  * <script type="text/javascript">
7837  */
7838
7839 /**
7840  * @class Roo.grid.AbstractSelectionModel
7841  * @extends Roo.util.Observable
7842  * @abstract
7843  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7844  * implemented by descendant classes.  This class should not be directly instantiated.
7845  * @constructor
7846  */
7847 Roo.grid.AbstractSelectionModel = function(){
7848     this.locked = false;
7849     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7850 };
7851
7852 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7853     /** @ignore Called by the grid automatically. Do not call directly. */
7854     init : function(grid){
7855         this.grid = grid;
7856         this.initEvents();
7857     },
7858
7859     /**
7860      * Locks the selections.
7861      */
7862     lock : function(){
7863         this.locked = true;
7864     },
7865
7866     /**
7867      * Unlocks the selections.
7868      */
7869     unlock : function(){
7870         this.locked = false;
7871     },
7872
7873     /**
7874      * Returns true if the selections are locked.
7875      * @return {Boolean}
7876      */
7877     isLocked : function(){
7878         return this.locked;
7879     }
7880 });/*
7881  * Based on:
7882  * Ext JS Library 1.1.1
7883  * Copyright(c) 2006-2007, Ext JS, LLC.
7884  *
7885  * Originally Released Under LGPL - original licence link has changed is not relivant.
7886  *
7887  * Fork - LGPL
7888  * <script type="text/javascript">
7889  */
7890 /**
7891  * @extends Roo.grid.AbstractSelectionModel
7892  * @class Roo.grid.RowSelectionModel
7893  * The default SelectionModel used by {@link Roo.grid.Grid}.
7894  * It supports multiple selections and keyboard selection/navigation. 
7895  * @constructor
7896  * @param {Object} config
7897  */
7898 Roo.grid.RowSelectionModel = function(config){
7899     Roo.apply(this, config);
7900     this.selections = new Roo.util.MixedCollection(false, function(o){
7901         return o.id;
7902     });
7903
7904     this.last = false;
7905     this.lastActive = false;
7906
7907     this.addEvents({
7908         /**
7909         * @event selectionchange
7910         * Fires when the selection changes
7911         * @param {SelectionModel} this
7912         */
7913        "selectionchange" : true,
7914        /**
7915         * @event afterselectionchange
7916         * Fires after the selection changes (eg. by key press or clicking)
7917         * @param {SelectionModel} this
7918         */
7919        "afterselectionchange" : true,
7920        /**
7921         * @event beforerowselect
7922         * Fires when a row is selected being selected, return false to cancel.
7923         * @param {SelectionModel} this
7924         * @param {Number} rowIndex The selected index
7925         * @param {Boolean} keepExisting False if other selections will be cleared
7926         */
7927        "beforerowselect" : true,
7928        /**
7929         * @event rowselect
7930         * Fires when a row is selected.
7931         * @param {SelectionModel} this
7932         * @param {Number} rowIndex The selected index
7933         * @param {Roo.data.Record} r The record
7934         */
7935        "rowselect" : true,
7936        /**
7937         * @event rowdeselect
7938         * Fires when a row is deselected.
7939         * @param {SelectionModel} this
7940         * @param {Number} rowIndex The selected index
7941         */
7942         "rowdeselect" : true
7943     });
7944     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7945     this.locked = false;
7946 };
7947
7948 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7949     /**
7950      * @cfg {Boolean} singleSelect
7951      * True to allow selection of only one row at a time (defaults to false)
7952      */
7953     singleSelect : false,
7954
7955     // private
7956     initEvents : function(){
7957
7958         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7959             this.grid.on("mousedown", this.handleMouseDown, this);
7960         }else{ // allow click to work like normal
7961             this.grid.on("rowclick", this.handleDragableRowClick, this);
7962         }
7963         // bootstrap does not have a view..
7964         var view = this.grid.view ? this.grid.view : this.grid;
7965         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7966             "up" : function(e){
7967                 if(!e.shiftKey){
7968                     this.selectPrevious(e.shiftKey);
7969                 }else if(this.last !== false && this.lastActive !== false){
7970                     var last = this.last;
7971                     this.selectRange(this.last,  this.lastActive-1);
7972                     view.focusRow(this.lastActive);
7973                     if(last !== false){
7974                         this.last = last;
7975                     }
7976                 }else{
7977                     this.selectFirstRow();
7978                 }
7979                 this.fireEvent("afterselectionchange", this);
7980             },
7981             "down" : function(e){
7982                 if(!e.shiftKey){
7983                     this.selectNext(e.shiftKey);
7984                 }else if(this.last !== false && this.lastActive !== false){
7985                     var last = this.last;
7986                     this.selectRange(this.last,  this.lastActive+1);
7987                     view.focusRow(this.lastActive);
7988                     if(last !== false){
7989                         this.last = last;
7990                     }
7991                 }else{
7992                     this.selectFirstRow();
7993                 }
7994                 this.fireEvent("afterselectionchange", this);
7995             },
7996             scope: this
7997         });
7998
7999          
8000         view.on("refresh", this.onRefresh, this);
8001         view.on("rowupdated", this.onRowUpdated, this);
8002         view.on("rowremoved", this.onRemove, this);
8003     },
8004
8005     // private
8006     onRefresh : function(){
8007         var ds = this.grid.ds, i, v = this.grid.view;
8008         var s = this.selections;
8009         s.each(function(r){
8010             if((i = ds.indexOfId(r.id)) != -1){
8011                 v.onRowSelect(i);
8012                 s.add(ds.getAt(i)); // updating the selection relate data
8013             }else{
8014                 s.remove(r);
8015             }
8016         });
8017     },
8018
8019     // private
8020     onRemove : function(v, index, r){
8021         this.selections.remove(r);
8022     },
8023
8024     // private
8025     onRowUpdated : function(v, index, r){
8026         if(this.isSelected(r)){
8027             v.onRowSelect(index);
8028         }
8029     },
8030
8031     /**
8032      * Select records.
8033      * @param {Array} records The records to select
8034      * @param {Boolean} keepExisting (optional) True to keep existing selections
8035      */
8036     selectRecords : function(records, keepExisting){
8037         if(!keepExisting){
8038             this.clearSelections();
8039         }
8040         var ds = this.grid.ds;
8041         for(var i = 0, len = records.length; i < len; i++){
8042             this.selectRow(ds.indexOf(records[i]), true);
8043         }
8044     },
8045
8046     /**
8047      * Gets the number of selected rows.
8048      * @return {Number}
8049      */
8050     getCount : function(){
8051         return this.selections.length;
8052     },
8053
8054     /**
8055      * Selects the first row in the grid.
8056      */
8057     selectFirstRow : function(){
8058         this.selectRow(0);
8059     },
8060
8061     /**
8062      * Select the last row.
8063      * @param {Boolean} keepExisting (optional) True to keep existing selections
8064      */
8065     selectLastRow : function(keepExisting){
8066         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8067     },
8068
8069     /**
8070      * Selects the row immediately following the last selected row.
8071      * @param {Boolean} keepExisting (optional) True to keep existing selections
8072      */
8073     selectNext : function(keepExisting){
8074         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8075             this.selectRow(this.last+1, keepExisting);
8076             var view = this.grid.view ? this.grid.view : this.grid;
8077             view.focusRow(this.last);
8078         }
8079     },
8080
8081     /**
8082      * Selects the row that precedes the last selected row.
8083      * @param {Boolean} keepExisting (optional) True to keep existing selections
8084      */
8085     selectPrevious : function(keepExisting){
8086         if(this.last){
8087             this.selectRow(this.last-1, keepExisting);
8088             var view = this.grid.view ? this.grid.view : this.grid;
8089             view.focusRow(this.last);
8090         }
8091     },
8092
8093     /**
8094      * Returns the selected records
8095      * @return {Array} Array of selected records
8096      */
8097     getSelections : function(){
8098         return [].concat(this.selections.items);
8099     },
8100
8101     /**
8102      * Returns the first selected record.
8103      * @return {Record}
8104      */
8105     getSelected : function(){
8106         return this.selections.itemAt(0);
8107     },
8108
8109
8110     /**
8111      * Clears all selections.
8112      */
8113     clearSelections : function(fast){
8114         if(this.locked) {
8115             return;
8116         }
8117         if(fast !== true){
8118             var ds = this.grid.ds;
8119             var s = this.selections;
8120             s.each(function(r){
8121                 this.deselectRow(ds.indexOfId(r.id));
8122             }, this);
8123             s.clear();
8124         }else{
8125             this.selections.clear();
8126         }
8127         this.last = false;
8128     },
8129
8130
8131     /**
8132      * Selects all rows.
8133      */
8134     selectAll : function(){
8135         if(this.locked) {
8136             return;
8137         }
8138         this.selections.clear();
8139         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8140             this.selectRow(i, true);
8141         }
8142     },
8143
8144     /**
8145      * Returns True if there is a selection.
8146      * @return {Boolean}
8147      */
8148     hasSelection : function(){
8149         return this.selections.length > 0;
8150     },
8151
8152     /**
8153      * Returns True if the specified row is selected.
8154      * @param {Number/Record} record The record or index of the record to check
8155      * @return {Boolean}
8156      */
8157     isSelected : function(index){
8158         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8159         return (r && this.selections.key(r.id) ? true : false);
8160     },
8161
8162     /**
8163      * Returns True if the specified record id is selected.
8164      * @param {String} id The id of record to check
8165      * @return {Boolean}
8166      */
8167     isIdSelected : function(id){
8168         return (this.selections.key(id) ? true : false);
8169     },
8170
8171     // private
8172     handleMouseDown : function(e, t)
8173     {
8174         var view = this.grid.view ? this.grid.view : this.grid;
8175         var rowIndex;
8176         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8177             return;
8178         };
8179         if(e.shiftKey && this.last !== false){
8180             var last = this.last;
8181             this.selectRange(last, rowIndex, e.ctrlKey);
8182             this.last = last; // reset the last
8183             view.focusRow(rowIndex);
8184         }else{
8185             var isSelected = this.isSelected(rowIndex);
8186             if(e.button !== 0 && isSelected){
8187                 view.focusRow(rowIndex);
8188             }else if(e.ctrlKey && isSelected){
8189                 this.deselectRow(rowIndex);
8190             }else if(!isSelected){
8191                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8192                 view.focusRow(rowIndex);
8193             }
8194         }
8195         this.fireEvent("afterselectionchange", this);
8196     },
8197     // private
8198     handleDragableRowClick :  function(grid, rowIndex, e) 
8199     {
8200         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8201             this.selectRow(rowIndex, false);
8202             var view = this.grid.view ? this.grid.view : this.grid;
8203             view.focusRow(rowIndex);
8204              this.fireEvent("afterselectionchange", this);
8205         }
8206     },
8207     
8208     /**
8209      * Selects multiple rows.
8210      * @param {Array} rows Array of the indexes of the row to select
8211      * @param {Boolean} keepExisting (optional) True to keep existing selections
8212      */
8213     selectRows : function(rows, keepExisting){
8214         if(!keepExisting){
8215             this.clearSelections();
8216         }
8217         for(var i = 0, len = rows.length; i < len; i++){
8218             this.selectRow(rows[i], true);
8219         }
8220     },
8221
8222     /**
8223      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8224      * @param {Number} startRow The index of the first row in the range
8225      * @param {Number} endRow The index of the last row in the range
8226      * @param {Boolean} keepExisting (optional) True to retain existing selections
8227      */
8228     selectRange : function(startRow, endRow, keepExisting){
8229         if(this.locked) {
8230             return;
8231         }
8232         if(!keepExisting){
8233             this.clearSelections();
8234         }
8235         if(startRow <= endRow){
8236             for(var i = startRow; i <= endRow; i++){
8237                 this.selectRow(i, true);
8238             }
8239         }else{
8240             for(var i = startRow; i >= endRow; i--){
8241                 this.selectRow(i, true);
8242             }
8243         }
8244     },
8245
8246     /**
8247      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8248      * @param {Number} startRow The index of the first row in the range
8249      * @param {Number} endRow The index of the last row in the range
8250      */
8251     deselectRange : function(startRow, endRow, preventViewNotify){
8252         if(this.locked) {
8253             return;
8254         }
8255         for(var i = startRow; i <= endRow; i++){
8256             this.deselectRow(i, preventViewNotify);
8257         }
8258     },
8259
8260     /**
8261      * Selects a row.
8262      * @param {Number} row The index of the row to select
8263      * @param {Boolean} keepExisting (optional) True to keep existing selections
8264      */
8265     selectRow : function(index, keepExisting, preventViewNotify){
8266         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8267             return;
8268         }
8269         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8270             if(!keepExisting || this.singleSelect){
8271                 this.clearSelections();
8272             }
8273             var r = this.grid.ds.getAt(index);
8274             this.selections.add(r);
8275             this.last = this.lastActive = index;
8276             if(!preventViewNotify){
8277                 var view = this.grid.view ? this.grid.view : this.grid;
8278                 view.onRowSelect(index);
8279             }
8280             this.fireEvent("rowselect", this, index, r);
8281             this.fireEvent("selectionchange", this);
8282         }
8283     },
8284
8285     /**
8286      * Deselects a row.
8287      * @param {Number} row The index of the row to deselect
8288      */
8289     deselectRow : function(index, preventViewNotify){
8290         if(this.locked) {
8291             return;
8292         }
8293         if(this.last == index){
8294             this.last = false;
8295         }
8296         if(this.lastActive == index){
8297             this.lastActive = false;
8298         }
8299         var r = this.grid.ds.getAt(index);
8300         this.selections.remove(r);
8301         if(!preventViewNotify){
8302             var view = this.grid.view ? this.grid.view : this.grid;
8303             view.onRowDeselect(index);
8304         }
8305         this.fireEvent("rowdeselect", this, index);
8306         this.fireEvent("selectionchange", this);
8307     },
8308
8309     // private
8310     restoreLast : function(){
8311         if(this._last){
8312             this.last = this._last;
8313         }
8314     },
8315
8316     // private
8317     acceptsNav : function(row, col, cm){
8318         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8319     },
8320
8321     // private
8322     onEditorKey : function(field, e){
8323         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8324         if(k == e.TAB){
8325             e.stopEvent();
8326             ed.completeEdit();
8327             if(e.shiftKey){
8328                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8329             }else{
8330                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8331             }
8332         }else if(k == e.ENTER && !e.ctrlKey){
8333             e.stopEvent();
8334             ed.completeEdit();
8335             if(e.shiftKey){
8336                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8337             }else{
8338                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8339             }
8340         }else if(k == e.ESC){
8341             ed.cancelEdit();
8342         }
8343         if(newCell){
8344             g.startEditing(newCell[0], newCell[1]);
8345         }
8346     }
8347 });/*
8348  * Based on:
8349  * Ext JS Library 1.1.1
8350  * Copyright(c) 2006-2007, Ext JS, LLC.
8351  *
8352  * Originally Released Under LGPL - original licence link has changed is not relivant.
8353  *
8354  * Fork - LGPL
8355  * <script type="text/javascript">
8356  */
8357  
8358
8359 /**
8360  * @class Roo.grid.ColumnModel
8361  * @extends Roo.util.Observable
8362  * This is the default implementation of a ColumnModel used by the Grid. It defines
8363  * the columns in the grid.
8364  * <br>Usage:<br>
8365  <pre><code>
8366  var colModel = new Roo.grid.ColumnModel([
8367         {header: "Ticker", width: 60, sortable: true, locked: true},
8368         {header: "Company Name", width: 150, sortable: true},
8369         {header: "Market Cap.", width: 100, sortable: true},
8370         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8371         {header: "Employees", width: 100, sortable: true, resizable: false}
8372  ]);
8373  </code></pre>
8374  * <p>
8375  
8376  * The config options listed for this class are options which may appear in each
8377  * individual column definition.
8378  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8379  * @constructor
8380  * @param {Object} config An Array of column config objects. See this class's
8381  * config objects for details.
8382 */
8383 Roo.grid.ColumnModel = function(config){
8384         /**
8385      * The config passed into the constructor
8386      */
8387     this.config = []; //config;
8388     this.lookup = {};
8389
8390     // if no id, create one
8391     // if the column does not have a dataIndex mapping,
8392     // map it to the order it is in the config
8393     for(var i = 0, len = config.length; i < len; i++){
8394         this.addColumn(config[i]);
8395         
8396     }
8397
8398     /**
8399      * The width of columns which have no width specified (defaults to 100)
8400      * @type Number
8401      */
8402     this.defaultWidth = 100;
8403
8404     /**
8405      * Default sortable of columns which have no sortable specified (defaults to false)
8406      * @type Boolean
8407      */
8408     this.defaultSortable = false;
8409
8410     this.addEvents({
8411         /**
8412              * @event widthchange
8413              * Fires when the width of a column changes.
8414              * @param {ColumnModel} this
8415              * @param {Number} columnIndex The column index
8416              * @param {Number} newWidth The new width
8417              */
8418             "widthchange": true,
8419         /**
8420              * @event headerchange
8421              * Fires when the text of a header changes.
8422              * @param {ColumnModel} this
8423              * @param {Number} columnIndex The column index
8424              * @param {Number} newText The new header text
8425              */
8426             "headerchange": true,
8427         /**
8428              * @event hiddenchange
8429              * Fires when a column is hidden or "unhidden".
8430              * @param {ColumnModel} this
8431              * @param {Number} columnIndex The column index
8432              * @param {Boolean} hidden true if hidden, false otherwise
8433              */
8434             "hiddenchange": true,
8435             /**
8436          * @event columnmoved
8437          * Fires when a column is moved.
8438          * @param {ColumnModel} this
8439          * @param {Number} oldIndex
8440          * @param {Number} newIndex
8441          */
8442         "columnmoved" : true,
8443         /**
8444          * @event columlockchange
8445          * Fires when a column's locked state is changed
8446          * @param {ColumnModel} this
8447          * @param {Number} colIndex
8448          * @param {Boolean} locked true if locked
8449          */
8450         "columnlockchange" : true
8451     });
8452     Roo.grid.ColumnModel.superclass.constructor.call(this);
8453 };
8454 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8455     /**
8456      * @cfg {String} header The header text to display in the Grid view.
8457      */
8458         /**
8459      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8460      */
8461         /**
8462      * @cfg {String} smHeader Header at Bootsrap Small width
8463      */
8464         /**
8465      * @cfg {String} mdHeader Header at Bootsrap Medium width
8466      */
8467         /**
8468      * @cfg {String} lgHeader Header at Bootsrap Large width
8469      */
8470         /**
8471      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8472      */
8473     /**
8474      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8475      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8476      * specified, the column's index is used as an index into the Record's data Array.
8477      */
8478     /**
8479      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8480      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8481      */
8482     /**
8483      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8484      * Defaults to the value of the {@link #defaultSortable} property.
8485      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8486      */
8487     /**
8488      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8489      */
8490     /**
8491      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8492      */
8493     /**
8494      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8495      */
8496     /**
8497      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8498      */
8499     /**
8500      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8501      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8502      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8503      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8504      */
8505        /**
8506      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8507      */
8508     /**
8509      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8510      */
8511     /**
8512      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8513      */
8514     /**
8515      * @cfg {String} cursor (Optional)
8516      */
8517     /**
8518      * @cfg {String} tooltip (Optional)
8519      */
8520     /**
8521      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8522      */
8523     /**
8524      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8525      */
8526     /**
8527      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8528      */
8529     /**
8530      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8531      */
8532         /**
8533      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8534      */
8535     /**
8536      * Returns the id of the column at the specified index.
8537      * @param {Number} index The column index
8538      * @return {String} the id
8539      */
8540     getColumnId : function(index){
8541         return this.config[index].id;
8542     },
8543
8544     /**
8545      * Returns the column for a specified id.
8546      * @param {String} id The column id
8547      * @return {Object} the column
8548      */
8549     getColumnById : function(id){
8550         return this.lookup[id];
8551     },
8552
8553     
8554     /**
8555      * Returns the column Object for a specified dataIndex.
8556      * @param {String} dataIndex The column dataIndex
8557      * @return {Object|Boolean} the column or false if not found
8558      */
8559     getColumnByDataIndex: function(dataIndex){
8560         var index = this.findColumnIndex(dataIndex);
8561         return index > -1 ? this.config[index] : false;
8562     },
8563     
8564     /**
8565      * Returns the index for a specified column id.
8566      * @param {String} id The column id
8567      * @return {Number} the index, or -1 if not found
8568      */
8569     getIndexById : function(id){
8570         for(var i = 0, len = this.config.length; i < len; i++){
8571             if(this.config[i].id == id){
8572                 return i;
8573             }
8574         }
8575         return -1;
8576     },
8577     
8578     /**
8579      * Returns the index for a specified column dataIndex.
8580      * @param {String} dataIndex The column dataIndex
8581      * @return {Number} the index, or -1 if not found
8582      */
8583     
8584     findColumnIndex : function(dataIndex){
8585         for(var i = 0, len = this.config.length; i < len; i++){
8586             if(this.config[i].dataIndex == dataIndex){
8587                 return i;
8588             }
8589         }
8590         return -1;
8591     },
8592     
8593     
8594     moveColumn : function(oldIndex, newIndex){
8595         var c = this.config[oldIndex];
8596         this.config.splice(oldIndex, 1);
8597         this.config.splice(newIndex, 0, c);
8598         this.dataMap = null;
8599         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8600     },
8601
8602     isLocked : function(colIndex){
8603         return this.config[colIndex].locked === true;
8604     },
8605
8606     setLocked : function(colIndex, value, suppressEvent){
8607         if(this.isLocked(colIndex) == value){
8608             return;
8609         }
8610         this.config[colIndex].locked = value;
8611         if(!suppressEvent){
8612             this.fireEvent("columnlockchange", this, colIndex, value);
8613         }
8614     },
8615
8616     getTotalLockedWidth : function(){
8617         var totalWidth = 0;
8618         for(var i = 0; i < this.config.length; i++){
8619             if(this.isLocked(i) && !this.isHidden(i)){
8620                 this.totalWidth += this.getColumnWidth(i);
8621             }
8622         }
8623         return totalWidth;
8624     },
8625
8626     getLockedCount : function(){
8627         for(var i = 0, len = this.config.length; i < len; i++){
8628             if(!this.isLocked(i)){
8629                 return i;
8630             }
8631         }
8632         
8633         return this.config.length;
8634     },
8635
8636     /**
8637      * Returns the number of columns.
8638      * @return {Number}
8639      */
8640     getColumnCount : function(visibleOnly){
8641         if(visibleOnly === true){
8642             var c = 0;
8643             for(var i = 0, len = this.config.length; i < len; i++){
8644                 if(!this.isHidden(i)){
8645                     c++;
8646                 }
8647             }
8648             return c;
8649         }
8650         return this.config.length;
8651     },
8652
8653     /**
8654      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8655      * @param {Function} fn
8656      * @param {Object} scope (optional)
8657      * @return {Array} result
8658      */
8659     getColumnsBy : function(fn, scope){
8660         var r = [];
8661         for(var i = 0, len = this.config.length; i < len; i++){
8662             var c = this.config[i];
8663             if(fn.call(scope||this, c, i) === true){
8664                 r[r.length] = c;
8665             }
8666         }
8667         return r;
8668     },
8669
8670     /**
8671      * Returns true if the specified column is sortable.
8672      * @param {Number} col The column index
8673      * @return {Boolean}
8674      */
8675     isSortable : function(col){
8676         if(typeof this.config[col].sortable == "undefined"){
8677             return this.defaultSortable;
8678         }
8679         return this.config[col].sortable;
8680     },
8681
8682     /**
8683      * Returns the rendering (formatting) function defined for the column.
8684      * @param {Number} col The column index.
8685      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8686      */
8687     getRenderer : function(col){
8688         if(!this.config[col].renderer){
8689             return Roo.grid.ColumnModel.defaultRenderer;
8690         }
8691         return this.config[col].renderer;
8692     },
8693
8694     /**
8695      * Sets the rendering (formatting) function for a column.
8696      * @param {Number} col The column index
8697      * @param {Function} fn The function to use to process the cell's raw data
8698      * to return HTML markup for the grid view. The render function is called with
8699      * the following parameters:<ul>
8700      * <li>Data value.</li>
8701      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8702      * <li>css A CSS style string to apply to the table cell.</li>
8703      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8704      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8705      * <li>Row index</li>
8706      * <li>Column index</li>
8707      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8708      */
8709     setRenderer : function(col, fn){
8710         this.config[col].renderer = fn;
8711     },
8712
8713     /**
8714      * Returns the width for the specified column.
8715      * @param {Number} col The column index
8716      * @param (optional) {String} gridSize bootstrap width size.
8717      * @return {Number}
8718      */
8719     getColumnWidth : function(col, gridSize)
8720         {
8721                 var cfg = this.config[col];
8722                 
8723                 if (typeof(gridSize) == 'undefined') {
8724                         return cfg.width * 1 || this.defaultWidth;
8725                 }
8726                 if (gridSize === false) { // if we set it..
8727                         return cfg.width || false;
8728                 }
8729                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8730                 
8731                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8732                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8733                                 continue;
8734                         }
8735                         return cfg[ sizes[i] ];
8736                 }
8737                 return 1;
8738                 
8739     },
8740
8741     /**
8742      * Sets the width for a column.
8743      * @param {Number} col The column index
8744      * @param {Number} width The new width
8745      */
8746     setColumnWidth : function(col, width, suppressEvent){
8747         this.config[col].width = width;
8748         this.totalWidth = null;
8749         if(!suppressEvent){
8750              this.fireEvent("widthchange", this, col, width);
8751         }
8752     },
8753
8754     /**
8755      * Returns the total width of all columns.
8756      * @param {Boolean} includeHidden True to include hidden column widths
8757      * @return {Number}
8758      */
8759     getTotalWidth : function(includeHidden){
8760         if(!this.totalWidth){
8761             this.totalWidth = 0;
8762             for(var i = 0, len = this.config.length; i < len; i++){
8763                 if(includeHidden || !this.isHidden(i)){
8764                     this.totalWidth += this.getColumnWidth(i);
8765                 }
8766             }
8767         }
8768         return this.totalWidth;
8769     },
8770
8771     /**
8772      * Returns the header for the specified column.
8773      * @param {Number} col The column index
8774      * @return {String}
8775      */
8776     getColumnHeader : function(col){
8777         return this.config[col].header;
8778     },
8779
8780     /**
8781      * Sets the header for a column.
8782      * @param {Number} col The column index
8783      * @param {String} header The new header
8784      */
8785     setColumnHeader : function(col, header){
8786         this.config[col].header = header;
8787         this.fireEvent("headerchange", this, col, header);
8788     },
8789
8790     /**
8791      * Returns the tooltip for the specified column.
8792      * @param {Number} col The column index
8793      * @return {String}
8794      */
8795     getColumnTooltip : function(col){
8796             return this.config[col].tooltip;
8797     },
8798     /**
8799      * Sets the tooltip for a column.
8800      * @param {Number} col The column index
8801      * @param {String} tooltip The new tooltip
8802      */
8803     setColumnTooltip : function(col, tooltip){
8804             this.config[col].tooltip = tooltip;
8805     },
8806
8807     /**
8808      * Returns the dataIndex for the specified column.
8809      * @param {Number} col The column index
8810      * @return {Number}
8811      */
8812     getDataIndex : function(col){
8813         return this.config[col].dataIndex;
8814     },
8815
8816     /**
8817      * Sets the dataIndex for a column.
8818      * @param {Number} col The column index
8819      * @param {Number} dataIndex The new dataIndex
8820      */
8821     setDataIndex : function(col, dataIndex){
8822         this.config[col].dataIndex = dataIndex;
8823     },
8824
8825     
8826     
8827     /**
8828      * Returns true if the cell is editable.
8829      * @param {Number} colIndex The column index
8830      * @param {Number} rowIndex The row index - this is nto actually used..?
8831      * @return {Boolean}
8832      */
8833     isCellEditable : function(colIndex, rowIndex){
8834         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8835     },
8836
8837     /**
8838      * Returns the editor defined for the cell/column.
8839      * return false or null to disable editing.
8840      * @param {Number} colIndex The column index
8841      * @param {Number} rowIndex The row index
8842      * @return {Object}
8843      */
8844     getCellEditor : function(colIndex, rowIndex){
8845         return this.config[colIndex].editor;
8846     },
8847
8848     /**
8849      * Sets if a column is editable.
8850      * @param {Number} col The column index
8851      * @param {Boolean} editable True if the column is editable
8852      */
8853     setEditable : function(col, editable){
8854         this.config[col].editable = editable;
8855     },
8856
8857
8858     /**
8859      * Returns true if the column is hidden.
8860      * @param {Number} colIndex The column index
8861      * @return {Boolean}
8862      */
8863     isHidden : function(colIndex){
8864         return this.config[colIndex].hidden;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column width cannot be changed
8870      */
8871     isFixed : function(colIndex){
8872         return this.config[colIndex].fixed;
8873     },
8874
8875     /**
8876      * Returns true if the column can be resized
8877      * @return {Boolean}
8878      */
8879     isResizable : function(colIndex){
8880         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8881     },
8882     /**
8883      * Sets if a column is hidden.
8884      * @param {Number} colIndex The column index
8885      * @param {Boolean} hidden True if the column is hidden
8886      */
8887     setHidden : function(colIndex, hidden){
8888         this.config[colIndex].hidden = hidden;
8889         this.totalWidth = null;
8890         this.fireEvent("hiddenchange", this, colIndex, hidden);
8891     },
8892
8893     /**
8894      * Sets the editor for a column.
8895      * @param {Number} col The column index
8896      * @param {Object} editor The editor object
8897      */
8898     setEditor : function(col, editor){
8899         this.config[col].editor = editor;
8900     },
8901     /**
8902      * Add a column (experimental...) - defaults to adding to the end..
8903      * @param {Object} config 
8904     */
8905     addColumn : function(c)
8906     {
8907     
8908         var i = this.config.length;
8909         this.config[i] = c;
8910         
8911         if(typeof c.dataIndex == "undefined"){
8912             c.dataIndex = i;
8913         }
8914         if(typeof c.renderer == "string"){
8915             c.renderer = Roo.util.Format[c.renderer];
8916         }
8917         if(typeof c.id == "undefined"){
8918             c.id = Roo.id();
8919         }
8920         if(c.editor && c.editor.xtype){
8921             c.editor  = Roo.factory(c.editor, Roo.grid);
8922         }
8923         if(c.editor && c.editor.isFormField){
8924             c.editor = new Roo.grid.GridEditor(c.editor);
8925         }
8926         this.lookup[c.id] = c;
8927     }
8928     
8929 });
8930
8931 Roo.grid.ColumnModel.defaultRenderer = function(value)
8932 {
8933     if(typeof value == "object") {
8934         return value;
8935     }
8936         if(typeof value == "string" && value.length < 1){
8937             return "&#160;";
8938         }
8939     
8940         return String.format("{0}", value);
8941 };
8942
8943 // Alias for backwards compatibility
8944 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8945 /*
8946  * Based on:
8947  * Ext JS Library 1.1.1
8948  * Copyright(c) 2006-2007, Ext JS, LLC.
8949  *
8950  * Originally Released Under LGPL - original licence link has changed is not relivant.
8951  *
8952  * Fork - LGPL
8953  * <script type="text/javascript">
8954  */
8955  
8956 /**
8957  * @class Roo.LoadMask
8958  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8959  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8960  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8961  * element's UpdateManager load indicator and will be destroyed after the initial load.
8962  * @constructor
8963  * Create a new LoadMask
8964  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8965  * @param {Object} config The config object
8966  */
8967 Roo.LoadMask = function(el, config){
8968     this.el = Roo.get(el);
8969     Roo.apply(this, config);
8970     if(this.store){
8971         this.store.on('beforeload', this.onBeforeLoad, this);
8972         this.store.on('load', this.onLoad, this);
8973         this.store.on('loadexception', this.onLoadException, this);
8974         this.removeMask = false;
8975     }else{
8976         var um = this.el.getUpdateManager();
8977         um.showLoadIndicator = false; // disable the default indicator
8978         um.on('beforeupdate', this.onBeforeLoad, this);
8979         um.on('update', this.onLoad, this);
8980         um.on('failure', this.onLoad, this);
8981         this.removeMask = true;
8982     }
8983 };
8984
8985 Roo.LoadMask.prototype = {
8986     /**
8987      * @cfg {Boolean} removeMask
8988      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8989      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8990      */
8991     removeMask : false,
8992     /**
8993      * @cfg {String} msg
8994      * The text to display in a centered loading message box (defaults to 'Loading...')
8995      */
8996     msg : 'Loading...',
8997     /**
8998      * @cfg {String} msgCls
8999      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9000      */
9001     msgCls : 'x-mask-loading',
9002
9003     /**
9004      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9005      * @type Boolean
9006      */
9007     disabled: false,
9008
9009     /**
9010      * Disables the mask to prevent it from being displayed
9011      */
9012     disable : function(){
9013        this.disabled = true;
9014     },
9015
9016     /**
9017      * Enables the mask so that it can be displayed
9018      */
9019     enable : function(){
9020         this.disabled = false;
9021     },
9022     
9023     onLoadException : function()
9024     {
9025         Roo.log(arguments);
9026         
9027         if (typeof(arguments[3]) != 'undefined') {
9028             Roo.MessageBox.alert("Error loading",arguments[3]);
9029         } 
9030         /*
9031         try {
9032             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9033                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9034             }   
9035         } catch(e) {
9036             
9037         }
9038         */
9039     
9040         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9041     },
9042     // private
9043     onLoad : function()
9044     {
9045         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9046     },
9047
9048     // private
9049     onBeforeLoad : function(){
9050         if(!this.disabled){
9051             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9052         }
9053     },
9054
9055     // private
9056     destroy : function(){
9057         if(this.store){
9058             this.store.un('beforeload', this.onBeforeLoad, this);
9059             this.store.un('load', this.onLoad, this);
9060             this.store.un('loadexception', this.onLoadException, this);
9061         }else{
9062             var um = this.el.getUpdateManager();
9063             um.un('beforeupdate', this.onBeforeLoad, this);
9064             um.un('update', this.onLoad, this);
9065             um.un('failure', this.onLoad, this);
9066         }
9067     }
9068 };/**
9069  * @class Roo.bootstrap.Table
9070  * @licence LGBL
9071  * @extends Roo.bootstrap.Component
9072  * @children Roo.bootstrap.TableBody
9073  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9074  * Similar to Roo.grid.Grid
9075  * <pre><code>
9076  var table = Roo.factory({
9077     xtype : 'Table',
9078     xns : Roo.bootstrap,
9079     autoSizeColumns: true,
9080     
9081     
9082     store : {
9083         xtype : 'Store',
9084         xns : Roo.data,
9085         remoteSort : true,
9086         sortInfo : { direction : 'ASC', field: 'name' },
9087         proxy : {
9088            xtype : 'HttpProxy',
9089            xns : Roo.data,
9090            method : 'GET',
9091            url : 'https://example.com/some.data.url.json'
9092         },
9093         reader : {
9094            xtype : 'JsonReader',
9095            xns : Roo.data,
9096            fields : [ 'id', 'name', whatever' ],
9097            id : 'id',
9098            root : 'data'
9099         }
9100     },
9101     cm : [
9102         {
9103             xtype : 'ColumnModel',
9104             xns : Roo.grid,
9105             align : 'center',
9106             cursor : 'pointer',
9107             dataIndex : 'is_in_group',
9108             header : "Name",
9109             sortable : true,
9110             renderer : function(v, x , r) {  
9111             
9112                 return String.format("{0}", v)
9113             }
9114             width : 3
9115         } // more columns..
9116     ],
9117     selModel : {
9118         xtype : 'RowSelectionModel',
9119         xns : Roo.bootstrap.Table
9120         // you can add listeners to catch selection change here....
9121     }
9122      
9123
9124  });
9125  // set any options
9126  grid.render(Roo.get("some-div"));
9127 </code></pre>
9128
9129 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9130
9131
9132
9133  *
9134  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9135  * @cfg {Roo.data.Store} store The data store to use
9136  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9137  * 
9138  * @cfg {String} cls table class
9139  *
9140  *
9141  * @cfg {string} empty_results  Text to display for no results 
9142  * @cfg {boolean} striped Should the rows be alternative striped
9143  * @cfg {boolean} bordered Add borders to the table
9144  * @cfg {boolean} hover Add hover highlighting
9145  * @cfg {boolean} condensed Format condensed
9146  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9147  *                also adds table-responsive (see bootstrap docs for details)
9148  * @cfg {Boolean} loadMask (true|false) default false
9149  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9150  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9151  * @cfg {Boolean} rowSelection (true|false) default false
9152  * @cfg {Boolean} cellSelection (true|false) default false
9153  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9154  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9155  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9156  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9157  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9158  *
9159  * 
9160  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9161  * 
9162  * @constructor
9163  * Create a new Table
9164  * @param {Object} config The config object
9165  */
9166
9167 Roo.bootstrap.Table = function(config)
9168 {
9169     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9170      
9171     // BC...
9172     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9173     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9174     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9175     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9176     
9177     this.view = this; // compat with grid.
9178     
9179     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9180     if (this.sm) {
9181         this.sm.grid = this;
9182         this.selModel = Roo.factory(this.sm, Roo.grid);
9183         this.sm = this.selModel;
9184         this.sm.xmodule = this.xmodule || false;
9185     }
9186     
9187     if (this.cm && typeof(this.cm.config) == 'undefined') {
9188         this.colModel = new Roo.grid.ColumnModel(this.cm);
9189         this.cm = this.colModel;
9190         this.cm.xmodule = this.xmodule || false;
9191     }
9192     if (this.store) {
9193         this.store= Roo.factory(this.store, Roo.data);
9194         this.ds = this.store;
9195         this.ds.xmodule = this.xmodule || false;
9196          
9197     }
9198     if (this.footer && this.store) {
9199         this.footer.dataSource = this.ds;
9200         this.footer = Roo.factory(this.footer);
9201     }
9202     
9203     /** @private */
9204     this.addEvents({
9205         /**
9206          * @event cellclick
9207          * Fires when a cell is clicked
9208          * @param {Roo.bootstrap.Table} this
9209          * @param {Roo.Element} el
9210          * @param {Number} rowIndex
9211          * @param {Number} columnIndex
9212          * @param {Roo.EventObject} e
9213          */
9214         "cellclick" : true,
9215         /**
9216          * @event celldblclick
9217          * Fires when a cell is double clicked
9218          * @param {Roo.bootstrap.Table} this
9219          * @param {Roo.Element} el
9220          * @param {Number} rowIndex
9221          * @param {Number} columnIndex
9222          * @param {Roo.EventObject} e
9223          */
9224         "celldblclick" : true,
9225         /**
9226          * @event rowclick
9227          * Fires when a row is clicked
9228          * @param {Roo.bootstrap.Table} this
9229          * @param {Roo.Element} el
9230          * @param {Number} rowIndex
9231          * @param {Roo.EventObject} e
9232          */
9233         "rowclick" : true,
9234         /**
9235          * @event rowdblclick
9236          * Fires when a row is double clicked
9237          * @param {Roo.bootstrap.Table} this
9238          * @param {Roo.Element} el
9239          * @param {Number} rowIndex
9240          * @param {Roo.EventObject} e
9241          */
9242         "rowdblclick" : true,
9243         /**
9244          * @event mouseover
9245          * Fires when a mouseover occur
9246          * @param {Roo.bootstrap.Table} this
9247          * @param {Roo.Element} el
9248          * @param {Number} rowIndex
9249          * @param {Number} columnIndex
9250          * @param {Roo.EventObject} e
9251          */
9252         "mouseover" : true,
9253         /**
9254          * @event mouseout
9255          * Fires when a mouseout occur
9256          * @param {Roo.bootstrap.Table} this
9257          * @param {Roo.Element} el
9258          * @param {Number} rowIndex
9259          * @param {Number} columnIndex
9260          * @param {Roo.EventObject} e
9261          */
9262         "mouseout" : true,
9263         /**
9264          * @event rowclass
9265          * Fires when a row is rendered, so you can change add a style to it.
9266          * @param {Roo.bootstrap.Table} this
9267          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9268          */
9269         'rowclass' : true,
9270           /**
9271          * @event rowsrendered
9272          * Fires when all the  rows have been rendered
9273          * @param {Roo.bootstrap.Table} this
9274          */
9275         'rowsrendered' : true,
9276         /**
9277          * @event contextmenu
9278          * The raw contextmenu event for the entire grid.
9279          * @param {Roo.EventObject} e
9280          */
9281         "contextmenu" : true,
9282         /**
9283          * @event rowcontextmenu
9284          * Fires when a row is right clicked
9285          * @param {Roo.bootstrap.Table} this
9286          * @param {Number} rowIndex
9287          * @param {Roo.EventObject} e
9288          */
9289         "rowcontextmenu" : true,
9290         /**
9291          * @event cellcontextmenu
9292          * Fires when a cell is right clicked
9293          * @param {Roo.bootstrap.Table} this
9294          * @param {Number} rowIndex
9295          * @param {Number} cellIndex
9296          * @param {Roo.EventObject} e
9297          */
9298          "cellcontextmenu" : true,
9299          /**
9300          * @event headercontextmenu
9301          * Fires when a header is right clicked
9302          * @param {Roo.bootstrap.Table} this
9303          * @param {Number} columnIndex
9304          * @param {Roo.EventObject} e
9305          */
9306         "headercontextmenu" : true,
9307         /**
9308          * @event mousedown
9309          * The raw mousedown event for the entire grid.
9310          * @param {Roo.EventObject} e
9311          */
9312         "mousedown" : true
9313         
9314     });
9315 };
9316
9317 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9318     
9319     cls: false,
9320     
9321     empty_results : '',
9322     striped : false,
9323     scrollBody : false,
9324     bordered: false,
9325     hover:  false,
9326     condensed : false,
9327     responsive : false,
9328     sm : false,
9329     cm : false,
9330     store : false,
9331     loadMask : false,
9332     footerShow : true,
9333     headerShow : true,
9334     enableColumnResize: true,
9335   
9336     rowSelection : false,
9337     cellSelection : false,
9338     layout : false,
9339
9340     minColumnWidth : 50,
9341     
9342     // Roo.Element - the tbody
9343     bodyEl: false,  // <tbody> Roo.Element - thead element    
9344     headEl: false,  // <thead> Roo.Element - thead element
9345     resizeProxy : false, // proxy element for dragging?
9346
9347
9348     
9349     container: false, // used by gridpanel...
9350     
9351     lazyLoad : false,
9352     
9353     CSS : Roo.util.CSS,
9354     
9355     auto_hide_footer : false,
9356     
9357     view: false, // actually points to this..
9358     
9359     getAutoCreate : function()
9360     {
9361         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9362         
9363         cfg = {
9364             tag: 'table',
9365             cls : 'table', 
9366             cn : []
9367         };
9368         // this get's auto added by panel.Grid
9369         if (this.scrollBody) {
9370             cfg.cls += ' table-body-fixed';
9371         }    
9372         if (this.striped) {
9373             cfg.cls += ' table-striped';
9374         }
9375         
9376         if (this.hover) {
9377             cfg.cls += ' table-hover';
9378         }
9379         if (this.bordered) {
9380             cfg.cls += ' table-bordered';
9381         }
9382         if (this.condensed) {
9383             cfg.cls += ' table-condensed';
9384         }
9385         
9386         if (this.responsive) {
9387             cfg.cls += ' table-responsive';
9388         }
9389         
9390         if (this.cls) {
9391             cfg.cls+=  ' ' +this.cls;
9392         }
9393         
9394         
9395         
9396         if (this.layout) {
9397             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9398         }
9399         
9400         if(this.store || this.cm){
9401             if(this.headerShow){
9402                 cfg.cn.push(this.renderHeader());
9403             }
9404             
9405             cfg.cn.push(this.renderBody());
9406             
9407             if(this.footerShow){
9408                 cfg.cn.push(this.renderFooter());
9409             }
9410             // where does this come from?
9411             //cfg.cls+=  ' TableGrid';
9412         }
9413         
9414         return { cn : [ cfg ] };
9415     },
9416     
9417     initEvents : function()
9418     {   
9419         if(!this.store || !this.cm){
9420             return;
9421         }
9422         if (this.selModel) {
9423             this.selModel.initEvents();
9424         }
9425         
9426         
9427         //Roo.log('initEvents with ds!!!!');
9428         
9429         this.bodyEl = this.el.select('tbody', true).first();
9430         this.headEl = this.el.select('thead', true).first();
9431         this.mainFoot = this.el.select('tfoot', true).first();
9432         
9433         
9434         
9435         
9436         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9437             e.on('click', this.sort, this);
9438         }, this);
9439         
9440         
9441         // why is this done????? = it breaks dialogs??
9442         //this.parent().el.setStyle('position', 'relative');
9443         
9444         
9445         if (this.footer) {
9446             this.footer.parentId = this.id;
9447             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9448             
9449             if(this.lazyLoad){
9450                 this.el.select('tfoot tr td').first().addClass('hide');
9451             }
9452         } 
9453         
9454         if(this.loadMask) {
9455             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9456         }
9457         
9458         this.store.on('load', this.onLoad, this);
9459         this.store.on('beforeload', this.onBeforeLoad, this);
9460         this.store.on('update', this.onUpdate, this);
9461         this.store.on('add', this.onAdd, this);
9462         this.store.on("clear", this.clear, this);
9463         
9464         this.el.on("contextmenu", this.onContextMenu, this);
9465         
9466         
9467         this.cm.on("headerchange", this.onHeaderChange, this);
9468         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9469
9470  //?? does bodyEl get replaced on render?
9471         this.bodyEl.on("click", this.onClick, this);
9472         this.bodyEl.on("dblclick", this.onDblClick, this);        
9473         this.bodyEl.on('scroll', this.onBodyScroll, this);
9474
9475         // guessing mainbody will work - this relays usually caught by selmodel at present.
9476         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9477   
9478   
9479         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9480         
9481   
9482         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9483             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9484         }
9485         
9486         this.initCSS();
9487     },
9488     // Compatibility with grid - we implement all the view features at present.
9489     getView : function()
9490     {
9491         return this;
9492     },
9493     
9494     initCSS : function()
9495     {
9496         
9497         
9498         var cm = this.cm, styles = [];
9499         this.CSS.removeStyleSheet(this.id + '-cssrules');
9500         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9501         // we can honour xs/sm/md/xl  as widths...
9502         // we first have to decide what widht we are currently at...
9503         var sz = Roo.getGridSize();
9504         
9505         var total = 0;
9506         var last = -1;
9507         var cols = []; // visable cols.
9508         var total_abs = 0;
9509         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9510             var w = cm.getColumnWidth(i, false);
9511             if(cm.isHidden(i)){
9512                 cols.push( { rel : false, abs : 0 });
9513                 continue;
9514             }
9515             if (w !== false) {
9516                 cols.push( { rel : false, abs : w });
9517                 total_abs += w;
9518                 last = i; // not really..
9519                 continue;
9520             }
9521             var w = cm.getColumnWidth(i, sz);
9522             if (w > 0) {
9523                 last = i
9524             }
9525             total += w;
9526             cols.push( { rel : w, abs : false });
9527         }
9528         
9529         var avail = this.bodyEl.dom.clientWidth - total_abs;
9530         
9531         var unitWidth = Math.floor(avail / total);
9532         var rem = avail - (unitWidth * total);
9533         
9534         var hidden, width, pos = 0 , splithide , left;
9535         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9536             
9537             hidden = 'display:none;';
9538             left = '';
9539             width  = 'width:0px;';
9540             splithide = '';
9541             if(!cm.isHidden(i)){
9542                 hidden = '';
9543                 
9544                 
9545                 // we can honour xs/sm/md/xl ?
9546                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9547                 if (w===0) {
9548                     hidden = 'display:none;';
9549                 }
9550                 // width should return a small number...
9551                 if (i == last) {
9552                     w+=rem; // add the remaining with..
9553                 }
9554                 pos += w;
9555                 left = "left:" + (pos -4) + "px;";
9556                 width = "width:" + w+ "px;";
9557                 
9558             }
9559             if (this.responsive) {
9560                 width = '';
9561                 left = '';
9562                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9563                 splithide = 'display: none;';
9564             }
9565             
9566             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9567             if (this.headEl) {
9568                 if (i == last) {
9569                     splithide = 'display:none;';
9570                 }
9571                 
9572                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9573                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9574                             // this is the popover version..
9575                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9576                 );
9577             }
9578             
9579         }
9580         //Roo.log(styles.join(''));
9581         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9582         
9583     },
9584     
9585     
9586     
9587     onContextMenu : function(e, t)
9588     {
9589         this.processEvent("contextmenu", e);
9590     },
9591     
9592     processEvent : function(name, e)
9593     {
9594         if (name != 'touchstart' ) {
9595             this.fireEvent(name, e);    
9596         }
9597         
9598         var t = e.getTarget();
9599         
9600         var cell = Roo.get(t);
9601         
9602         if(!cell){
9603             return;
9604         }
9605         
9606         if(cell.findParent('tfoot', false, true)){
9607             return;
9608         }
9609         
9610         if(cell.findParent('thead', false, true)){
9611             
9612             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9613                 cell = Roo.get(t).findParent('th', false, true);
9614                 if (!cell) {
9615                     Roo.log("failed to find th in thead?");
9616                     Roo.log(e.getTarget());
9617                     return;
9618                 }
9619             }
9620             
9621             var cellIndex = cell.dom.cellIndex;
9622             
9623             var ename = name == 'touchstart' ? 'click' : name;
9624             this.fireEvent("header" + ename, this, cellIndex, e);
9625             
9626             return;
9627         }
9628         
9629         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9630             cell = Roo.get(t).findParent('td', false, true);
9631             if (!cell) {
9632                 Roo.log("failed to find th in tbody?");
9633                 Roo.log(e.getTarget());
9634                 return;
9635             }
9636         }
9637         
9638         var row = cell.findParent('tr', false, true);
9639         var cellIndex = cell.dom.cellIndex;
9640         var rowIndex = row.dom.rowIndex - 1;
9641         
9642         if(row !== false){
9643             
9644             this.fireEvent("row" + name, this, rowIndex, e);
9645             
9646             if(cell !== false){
9647             
9648                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9649             }
9650         }
9651         
9652     },
9653     
9654     onMouseover : function(e, el)
9655     {
9656         var cell = Roo.get(el);
9657         
9658         if(!cell){
9659             return;
9660         }
9661         
9662         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9663             cell = cell.findParent('td', false, true);
9664         }
9665         
9666         var row = cell.findParent('tr', false, true);
9667         var cellIndex = cell.dom.cellIndex;
9668         var rowIndex = row.dom.rowIndex - 1; // start from 0
9669         
9670         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9671         
9672     },
9673     
9674     onMouseout : function(e, el)
9675     {
9676         var cell = Roo.get(el);
9677         
9678         if(!cell){
9679             return;
9680         }
9681         
9682         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9683             cell = cell.findParent('td', false, true);
9684         }
9685         
9686         var row = cell.findParent('tr', false, true);
9687         var cellIndex = cell.dom.cellIndex;
9688         var rowIndex = row.dom.rowIndex - 1; // start from 0
9689         
9690         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9691         
9692     },
9693     
9694     onClick : function(e, el)
9695     {
9696         var cell = Roo.get(el);
9697         
9698         if(!cell || (!this.cellSelection && !this.rowSelection)){
9699             return;
9700         }
9701         
9702         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9703             cell = cell.findParent('td', false, true);
9704         }
9705         
9706         if(!cell || typeof(cell) == 'undefined'){
9707             return;
9708         }
9709         
9710         var row = cell.findParent('tr', false, true);
9711         
9712         if(!row || typeof(row) == 'undefined'){
9713             return;
9714         }
9715         
9716         var cellIndex = cell.dom.cellIndex;
9717         var rowIndex = this.getRowIndex(row);
9718         
9719         // why??? - should these not be based on SelectionModel?
9720         //if(this.cellSelection){
9721             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9722         //}
9723         
9724         //if(this.rowSelection){
9725             this.fireEvent('rowclick', this, row, rowIndex, e);
9726         //}
9727          
9728     },
9729         
9730     onDblClick : function(e,el)
9731     {
9732         var cell = Roo.get(el);
9733         
9734         if(!cell || (!this.cellSelection && !this.rowSelection)){
9735             return;
9736         }
9737         
9738         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9739             cell = cell.findParent('td', false, true);
9740         }
9741         
9742         if(!cell || typeof(cell) == 'undefined'){
9743             return;
9744         }
9745         
9746         var row = cell.findParent('tr', false, true);
9747         
9748         if(!row || typeof(row) == 'undefined'){
9749             return;
9750         }
9751         
9752         var cellIndex = cell.dom.cellIndex;
9753         var rowIndex = this.getRowIndex(row);
9754         
9755         if(this.cellSelection){
9756             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9757         }
9758         
9759         if(this.rowSelection){
9760             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9761         }
9762     },
9763     findRowIndex : function(el)
9764     {
9765         var cell = Roo.get(el);
9766         if(!cell) {
9767             return false;
9768         }
9769         var row = cell.findParent('tr', false, true);
9770         
9771         if(!row || typeof(row) == 'undefined'){
9772             return false;
9773         }
9774         return this.getRowIndex(row);
9775     },
9776     sort : function(e,el)
9777     {
9778         var col = Roo.get(el);
9779         
9780         if(!col.hasClass('sortable')){
9781             return;
9782         }
9783         
9784         var sort = col.attr('sort');
9785         var dir = 'ASC';
9786         
9787         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9788             dir = 'DESC';
9789         }
9790         
9791         this.store.sortInfo = {field : sort, direction : dir};
9792         
9793         if (this.footer) {
9794             Roo.log("calling footer first");
9795             this.footer.onClick('first');
9796         } else {
9797         
9798             this.store.load({ params : { start : 0 } });
9799         }
9800     },
9801     
9802     renderHeader : function()
9803     {
9804         var header = {
9805             tag: 'thead',
9806             cn : []
9807         };
9808         
9809         var cm = this.cm;
9810         this.totalWidth = 0;
9811         
9812         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9813             
9814             var config = cm.config[i];
9815             
9816             var c = {
9817                 tag: 'th',
9818                 cls : 'x-hcol-' + i,
9819                 style : '',
9820                 
9821                 html: cm.getColumnHeader(i)
9822             };
9823             
9824             var tooltip = cm.getColumnTooltip(i);
9825             if (tooltip) {
9826                 c.tooltip = tooltip;
9827             }
9828             
9829             
9830             var hh = '';
9831             
9832             if(typeof(config.sortable) != 'undefined' && config.sortable){
9833                 c.cls += ' sortable';
9834                 c.html = '<i class="fa"></i>' + c.html;
9835             }
9836             
9837             // could use BS4 hidden-..-down 
9838             
9839             if(typeof(config.lgHeader) != 'undefined'){
9840                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9841             }
9842             
9843             if(typeof(config.mdHeader) != 'undefined'){
9844                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9845             }
9846             
9847             if(typeof(config.smHeader) != 'undefined'){
9848                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9849             }
9850             
9851             if(typeof(config.xsHeader) != 'undefined'){
9852                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9853             }
9854             
9855             if(hh.length){
9856                 c.html = hh;
9857             }
9858             
9859             if(typeof(config.tooltip) != 'undefined'){
9860                 c.tooltip = config.tooltip;
9861             }
9862             
9863             if(typeof(config.colspan) != 'undefined'){
9864                 c.colspan = config.colspan;
9865             }
9866             
9867             // hidden is handled by CSS now
9868             
9869             if(typeof(config.dataIndex) != 'undefined'){
9870                 c.sort = config.dataIndex;
9871             }
9872             
9873            
9874             
9875             if(typeof(config.align) != 'undefined' && config.align.length){
9876                 c.style += ' text-align:' + config.align + ';';
9877             }
9878             
9879             /* width is done in CSS
9880              *if(typeof(config.width) != 'undefined'){
9881                 c.style += ' width:' + config.width + 'px;';
9882                 this.totalWidth += config.width;
9883             } else {
9884                 this.totalWidth += 100; // assume minimum of 100 per column?
9885             }
9886             */
9887             
9888             if(typeof(config.cls) != 'undefined'){
9889                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9890             }
9891             // this is the bit that doesnt reall work at all...
9892             
9893             if (this.responsive) {
9894                  
9895             
9896                 ['xs','sm','md','lg'].map(function(size){
9897                     
9898                     if(typeof(config[size]) == 'undefined'){
9899                         return;
9900                     }
9901                      
9902                     if (!config[size]) { // 0 = hidden
9903                         // BS 4 '0' is treated as hide that column and below.
9904                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9905                         return;
9906                     }
9907                     
9908                     c.cls += ' col-' + size + '-' + config[size] + (
9909                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9910                     );
9911                     
9912                     
9913                 });
9914             }
9915             // at the end?
9916             
9917             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9918             
9919             
9920             
9921             
9922             header.cn.push(c)
9923         }
9924         
9925         return header;
9926     },
9927     
9928     renderBody : function()
9929     {
9930         var body = {
9931             tag: 'tbody',
9932             cn : [
9933                 {
9934                     tag: 'tr',
9935                     cn : [
9936                         {
9937                             tag : 'td',
9938                             colspan :  this.cm.getColumnCount()
9939                         }
9940                     ]
9941                 }
9942             ]
9943         };
9944         
9945         return body;
9946     },
9947     
9948     renderFooter : function()
9949     {
9950         var footer = {
9951             tag: 'tfoot',
9952             cn : [
9953                 {
9954                     tag: 'tr',
9955                     cn : [
9956                         {
9957                             tag : 'td',
9958                             colspan :  this.cm.getColumnCount()
9959                         }
9960                     ]
9961                 }
9962             ]
9963         };
9964         
9965         return footer;
9966     },
9967     
9968     
9969     
9970     onLoad : function()
9971     {
9972 //        Roo.log('ds onload');
9973         this.clear();
9974         
9975         var _this = this;
9976         var cm = this.cm;
9977         var ds = this.store;
9978         
9979         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9980             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9981             if (_this.store.sortInfo) {
9982                     
9983                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9984                     e.select('i', true).addClass(['fa-arrow-up']);
9985                 }
9986                 
9987                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9988                     e.select('i', true).addClass(['fa-arrow-down']);
9989                 }
9990             }
9991         });
9992         
9993         var tbody =  this.bodyEl;
9994               
9995         if(ds.getCount() > 0){
9996             ds.data.each(function(d,rowIndex){
9997                 var row =  this.renderRow(cm, ds, rowIndex);
9998                 
9999                 tbody.createChild(row);
10000                 
10001                 var _this = this;
10002                 
10003                 if(row.cellObjects.length){
10004                     Roo.each(row.cellObjects, function(r){
10005                         _this.renderCellObject(r);
10006                     })
10007                 }
10008                 
10009             }, this);
10010         } else if (this.empty_results.length) {
10011             this.el.mask(this.empty_results, 'no-spinner');
10012         }
10013         
10014         var tfoot = this.el.select('tfoot', true).first();
10015         
10016         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10017             
10018             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10019             
10020             var total = this.ds.getTotalCount();
10021             
10022             if(this.footer.pageSize < total){
10023                 this.mainFoot.show();
10024             }
10025         }
10026         
10027         Roo.each(this.el.select('tbody td', true).elements, function(e){
10028             e.on('mouseover', _this.onMouseover, _this);
10029         });
10030         
10031         Roo.each(this.el.select('tbody td', true).elements, function(e){
10032             e.on('mouseout', _this.onMouseout, _this);
10033         });
10034         this.fireEvent('rowsrendered', this);
10035         
10036         this.autoSize();
10037         
10038         this.initCSS(); /// resize cols
10039
10040         
10041     },
10042     
10043     
10044     onUpdate : function(ds,record)
10045     {
10046         this.refreshRow(record);
10047         this.autoSize();
10048     },
10049     
10050     onRemove : function(ds, record, index, isUpdate){
10051         if(isUpdate !== true){
10052             this.fireEvent("beforerowremoved", this, index, record);
10053         }
10054         var bt = this.bodyEl.dom;
10055         
10056         var rows = this.el.select('tbody > tr', true).elements;
10057         
10058         if(typeof(rows[index]) != 'undefined'){
10059             bt.removeChild(rows[index].dom);
10060         }
10061         
10062 //        if(bt.rows[index]){
10063 //            bt.removeChild(bt.rows[index]);
10064 //        }
10065         
10066         if(isUpdate !== true){
10067             //this.stripeRows(index);
10068             //this.syncRowHeights(index, index);
10069             //this.layout();
10070             this.fireEvent("rowremoved", this, index, record);
10071         }
10072     },
10073     
10074     onAdd : function(ds, records, rowIndex)
10075     {
10076         //Roo.log('on Add called');
10077         // - note this does not handle multiple adding very well..
10078         var bt = this.bodyEl.dom;
10079         for (var i =0 ; i < records.length;i++) {
10080             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10081             //Roo.log(records[i]);
10082             //Roo.log(this.store.getAt(rowIndex+i));
10083             this.insertRow(this.store, rowIndex + i, false);
10084             return;
10085         }
10086         
10087     },
10088     
10089     
10090     refreshRow : function(record){
10091         var ds = this.store, index;
10092         if(typeof record == 'number'){
10093             index = record;
10094             record = ds.getAt(index);
10095         }else{
10096             index = ds.indexOf(record);
10097             if (index < 0) {
10098                 return; // should not happen - but seems to 
10099             }
10100         }
10101         this.insertRow(ds, index, true);
10102         this.autoSize();
10103         this.onRemove(ds, record, index+1, true);
10104         this.autoSize();
10105         //this.syncRowHeights(index, index);
10106         //this.layout();
10107         this.fireEvent("rowupdated", this, index, record);
10108     },
10109     // private - called by RowSelection
10110     onRowSelect : function(rowIndex){
10111         var row = this.getRowDom(rowIndex);
10112         row.addClass(['bg-info','info']);
10113     },
10114     // private - called by RowSelection
10115     onRowDeselect : function(rowIndex)
10116     {
10117         if (rowIndex < 0) {
10118             return;
10119         }
10120         var row = this.getRowDom(rowIndex);
10121         row.removeClass(['bg-info','info']);
10122     },
10123       /**
10124      * Focuses the specified row.
10125      * @param {Number} row The row index
10126      */
10127     focusRow : function(row)
10128     {
10129         //Roo.log('GridView.focusRow');
10130         var x = this.bodyEl.dom.scrollLeft;
10131         this.focusCell(row, 0, false);
10132         this.bodyEl.dom.scrollLeft = x;
10133
10134     },
10135      /**
10136      * Focuses the specified cell.
10137      * @param {Number} row The row index
10138      * @param {Number} col The column index
10139      * @param {Boolean} hscroll false to disable horizontal scrolling
10140      */
10141     focusCell : function(row, col, hscroll)
10142     {
10143         //Roo.log('GridView.focusCell');
10144         var el = this.ensureVisible(row, col, hscroll);
10145         // not sure what focusEL achives = it's a <a> pos relative 
10146         //this.focusEl.alignTo(el, "tl-tl");
10147         //if(Roo.isGecko){
10148         //    this.focusEl.focus();
10149         //}else{
10150         //    this.focusEl.focus.defer(1, this.focusEl);
10151         //}
10152     },
10153     
10154      /**
10155      * Scrolls the specified cell into view
10156      * @param {Number} row The row index
10157      * @param {Number} col The column index
10158      * @param {Boolean} hscroll false to disable horizontal scrolling
10159      */
10160     ensureVisible : function(row, col, hscroll)
10161     {
10162         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10163         //return null; //disable for testing.
10164         if(typeof row != "number"){
10165             row = row.rowIndex;
10166         }
10167         if(row < 0 && row >= this.ds.getCount()){
10168             return  null;
10169         }
10170         col = (col !== undefined ? col : 0);
10171         var cm = this.cm;
10172         while(cm.isHidden(col)){
10173             col++;
10174         }
10175
10176         var el = this.getCellDom(row, col);
10177         if(!el){
10178             return null;
10179         }
10180         var c = this.bodyEl.dom;
10181
10182         var ctop = parseInt(el.offsetTop, 10);
10183         var cleft = parseInt(el.offsetLeft, 10);
10184         var cbot = ctop + el.offsetHeight;
10185         var cright = cleft + el.offsetWidth;
10186
10187         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10188         var ch = 0; //?? header is not withing the area?
10189         var stop = parseInt(c.scrollTop, 10);
10190         var sleft = parseInt(c.scrollLeft, 10);
10191         var sbot = stop + ch;
10192         var sright = sleft + c.clientWidth;
10193         /*
10194         Roo.log('GridView.ensureVisible:' +
10195                 ' ctop:' + ctop +
10196                 ' c.clientHeight:' + c.clientHeight +
10197                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10198                 ' stop:' + stop +
10199                 ' cbot:' + cbot +
10200                 ' sbot:' + sbot +
10201                 ' ch:' + ch  
10202                 );
10203         */
10204         if(ctop < stop){
10205             c.scrollTop = ctop;
10206             //Roo.log("set scrolltop to ctop DISABLE?");
10207         }else if(cbot > sbot){
10208             //Roo.log("set scrolltop to cbot-ch");
10209             c.scrollTop = cbot-ch;
10210         }
10211
10212         if(hscroll !== false){
10213             if(cleft < sleft){
10214                 c.scrollLeft = cleft;
10215             }else if(cright > sright){
10216                 c.scrollLeft = cright-c.clientWidth;
10217             }
10218         }
10219
10220         return el;
10221     },
10222     
10223     
10224     insertRow : function(dm, rowIndex, isUpdate){
10225         
10226         if(!isUpdate){
10227             this.fireEvent("beforerowsinserted", this, rowIndex);
10228         }
10229             //var s = this.getScrollState();
10230         var row = this.renderRow(this.cm, this.store, rowIndex);
10231         // insert before rowIndex..
10232         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10233         
10234         var _this = this;
10235                 
10236         if(row.cellObjects.length){
10237             Roo.each(row.cellObjects, function(r){
10238                 _this.renderCellObject(r);
10239             })
10240         }
10241             
10242         if(!isUpdate){
10243             this.fireEvent("rowsinserted", this, rowIndex);
10244             //this.syncRowHeights(firstRow, lastRow);
10245             //this.stripeRows(firstRow);
10246             //this.layout();
10247         }
10248         
10249     },
10250     
10251     
10252     getRowDom : function(rowIndex)
10253     {
10254         var rows = this.el.select('tbody > tr', true).elements;
10255         
10256         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10257         
10258     },
10259     getCellDom : function(rowIndex, colIndex)
10260     {
10261         var row = this.getRowDom(rowIndex);
10262         if (row === false) {
10263             return false;
10264         }
10265         var cols = row.select('td', true).elements;
10266         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10267         
10268     },
10269     
10270     // returns the object tree for a tr..
10271   
10272     
10273     renderRow : function(cm, ds, rowIndex) 
10274     {
10275         var d = ds.getAt(rowIndex);
10276         
10277         var row = {
10278             tag : 'tr',
10279             cls : 'x-row-' + rowIndex,
10280             cn : []
10281         };
10282             
10283         var cellObjects = [];
10284         
10285         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10286             var config = cm.config[i];
10287             
10288             var renderer = cm.getRenderer(i);
10289             var value = '';
10290             var id = false;
10291             
10292             if(typeof(renderer) !== 'undefined'){
10293                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10294             }
10295             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10296             // and are rendered into the cells after the row is rendered - using the id for the element.
10297             
10298             if(typeof(value) === 'object'){
10299                 id = Roo.id();
10300                 cellObjects.push({
10301                     container : id,
10302                     cfg : value 
10303                 })
10304             }
10305             
10306             var rowcfg = {
10307                 record: d,
10308                 rowIndex : rowIndex,
10309                 colIndex : i,
10310                 rowClass : ''
10311             };
10312
10313             this.fireEvent('rowclass', this, rowcfg);
10314             
10315             var td = {
10316                 tag: 'td',
10317                 // this might end up displaying HTML?
10318                 // this is too messy... - better to only do it on columsn you know are going to be too long
10319                 //tooltip : (typeof(value) === 'object') ? '' : value,
10320                 cls : rowcfg.rowClass + ' x-col-' + i,
10321                 style: '',
10322                 html: (typeof(value) === 'object') ? '' : value
10323             };
10324             
10325             if (id) {
10326                 td.id = id;
10327             }
10328             
10329             if(typeof(config.colspan) != 'undefined'){
10330                 td.colspan = config.colspan;
10331             }
10332             
10333             
10334             
10335             if(typeof(config.align) != 'undefined' && config.align.length){
10336                 td.style += ' text-align:' + config.align + ';';
10337             }
10338             if(typeof(config.valign) != 'undefined' && config.valign.length){
10339                 td.style += ' vertical-align:' + config.valign + ';';
10340             }
10341             /*
10342             if(typeof(config.width) != 'undefined'){
10343                 td.style += ' width:' +  config.width + 'px;';
10344             }
10345             */
10346             
10347             if(typeof(config.cursor) != 'undefined'){
10348                 td.style += ' cursor:' +  config.cursor + ';';
10349             }
10350             
10351             if(typeof(config.cls) != 'undefined'){
10352                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10353             }
10354             if (this.responsive) {
10355                 ['xs','sm','md','lg'].map(function(size){
10356                     
10357                     if(typeof(config[size]) == 'undefined'){
10358                         return;
10359                     }
10360                     
10361                     
10362                       
10363                     if (!config[size]) { // 0 = hidden
10364                         // BS 4 '0' is treated as hide that column and below.
10365                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10366                         return;
10367                     }
10368                     
10369                     td.cls += ' col-' + size + '-' + config[size] + (
10370                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10371                     );
10372                      
10373     
10374                 });
10375             }
10376             row.cn.push(td);
10377            
10378         }
10379         
10380         row.cellObjects = cellObjects;
10381         
10382         return row;
10383           
10384     },
10385     
10386     
10387     
10388     onBeforeLoad : function()
10389     {
10390         this.el.unmask(); // if needed.
10391     },
10392      /**
10393      * Remove all rows
10394      */
10395     clear : function()
10396     {
10397         this.el.select('tbody', true).first().dom.innerHTML = '';
10398     },
10399     /**
10400      * Show or hide a row.
10401      * @param {Number} rowIndex to show or hide
10402      * @param {Boolean} state hide
10403      */
10404     setRowVisibility : function(rowIndex, state)
10405     {
10406         var bt = this.bodyEl.dom;
10407         
10408         var rows = this.el.select('tbody > tr', true).elements;
10409         
10410         if(typeof(rows[rowIndex]) == 'undefined'){
10411             return;
10412         }
10413         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10414         
10415     },
10416     
10417     
10418     getSelectionModel : function(){
10419         if(!this.selModel){
10420             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10421         }
10422         return this.selModel;
10423     },
10424     /*
10425      * Render the Roo.bootstrap object from renderder
10426      */
10427     renderCellObject : function(r)
10428     {
10429         var _this = this;
10430         
10431         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10432         
10433         var t = r.cfg.render(r.container);
10434         
10435         if(r.cfg.cn){
10436             Roo.each(r.cfg.cn, function(c){
10437                 var child = {
10438                     container: t.getChildContainer(),
10439                     cfg: c
10440                 };
10441                 _this.renderCellObject(child);
10442             })
10443         }
10444     },
10445     /**
10446      * get the Row Index from a dom element.
10447      * @param {Roo.Element} row The row to look for
10448      * @returns {Number} the row
10449      */
10450     getRowIndex : function(row)
10451     {
10452         var rowIndex = -1;
10453         
10454         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10455             if(el != row){
10456                 return;
10457             }
10458             
10459             rowIndex = index;
10460         });
10461         
10462         return rowIndex;
10463     },
10464     /**
10465      * get the header TH element for columnIndex
10466      * @param {Number} columnIndex
10467      * @returns {Roo.Element}
10468      */
10469     getHeaderIndex: function(colIndex)
10470     {
10471         var cols = this.headEl.select('th', true).elements;
10472         return cols[colIndex]; 
10473     },
10474     /**
10475      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10476      * @param {domElement} cell to look for
10477      * @returns {Number} the column
10478      */
10479     getCellIndex : function(cell)
10480     {
10481         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10482         if(id){
10483             return parseInt(id[1], 10);
10484         }
10485         return 0;
10486     },
10487      /**
10488      * Returns the grid's underlying element = used by panel.Grid
10489      * @return {Element} The element
10490      */
10491     getGridEl : function(){
10492         return this.el;
10493     },
10494      /**
10495      * Forces a resize - used by panel.Grid
10496      * @return {Element} The element
10497      */
10498     autoSize : function()
10499     {
10500         //var ctr = Roo.get(this.container.dom.parentElement);
10501         var ctr = Roo.get(this.el.dom);
10502         
10503         var thd = this.getGridEl().select('thead',true).first();
10504         var tbd = this.getGridEl().select('tbody', true).first();
10505         var tfd = this.getGridEl().select('tfoot', true).first();
10506         
10507         var cw = ctr.getWidth();
10508         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10509         
10510         if (tbd) {
10511             
10512             tbd.setWidth(ctr.getWidth());
10513             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10514             // this needs fixing for various usage - currently only hydra job advers I think..
10515             //tdb.setHeight(
10516             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10517             //); 
10518             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10519             cw -= barsize;
10520         }
10521         cw = Math.max(cw, this.totalWidth);
10522         this.getGridEl().select('tbody tr',true).setWidth(cw);
10523         this.initCSS();
10524         
10525         // resize 'expandable coloumn?
10526         
10527         return; // we doe not have a view in this design..
10528         
10529     },
10530     onBodyScroll: function()
10531     {
10532         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10533         if(this.headEl){
10534             this.headEl.setStyle({
10535                 'position' : 'relative',
10536                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10537             });
10538         }
10539         
10540         if(this.lazyLoad){
10541             
10542             var scrollHeight = this.bodyEl.dom.scrollHeight;
10543             
10544             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10545             
10546             var height = this.bodyEl.getHeight();
10547             
10548             if(scrollHeight - height == scrollTop) {
10549                 
10550                 var total = this.ds.getTotalCount();
10551                 
10552                 if(this.footer.cursor + this.footer.pageSize < total){
10553                     
10554                     this.footer.ds.load({
10555                         params : {
10556                             start : this.footer.cursor + this.footer.pageSize,
10557                             limit : this.footer.pageSize
10558                         },
10559                         add : true
10560                     });
10561                 }
10562             }
10563             
10564         }
10565     },
10566     onColumnSplitterMoved : function(i, diff)
10567     {
10568         this.userResized = true;
10569         
10570         var cm = this.colModel;
10571         
10572         var w = this.getHeaderIndex(i).getWidth() + diff;
10573         
10574         
10575         cm.setColumnWidth(i, w, true);
10576         this.initCSS();
10577         //var cid = cm.getColumnId(i); << not used in this version?
10578        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10579         
10580         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10581         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10582         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10583 */
10584         //this.updateSplitters();
10585         //this.layout(); << ??
10586         this.fireEvent("columnresize", i, w);
10587     },
10588     onHeaderChange : function()
10589     {
10590         var header = this.renderHeader();
10591         var table = this.el.select('table', true).first();
10592         
10593         this.headEl.remove();
10594         this.headEl = table.createChild(header, this.bodyEl, false);
10595         
10596         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10597             e.on('click', this.sort, this);
10598         }, this);
10599         
10600         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10601             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10602         }
10603         
10604     },
10605     
10606     onHiddenChange : function(colModel, colIndex, hidden)
10607     {
10608         /*
10609         this.cm.setHidden()
10610         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10611         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10612         
10613         this.CSS.updateRule(thSelector, "display", "");
10614         this.CSS.updateRule(tdSelector, "display", "");
10615         
10616         if(hidden){
10617             this.CSS.updateRule(thSelector, "display", "none");
10618             this.CSS.updateRule(tdSelector, "display", "none");
10619         }
10620         */
10621         // onload calls initCSS()
10622         this.onHeaderChange();
10623         this.onLoad();
10624     },
10625     
10626     setColumnWidth: function(col_index, width)
10627     {
10628         // width = "md-2 xs-2..."
10629         if(!this.colModel.config[col_index]) {
10630             return;
10631         }
10632         
10633         var w = width.split(" ");
10634         
10635         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10636         
10637         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10638         
10639         
10640         for(var j = 0; j < w.length; j++) {
10641             
10642             if(!w[j]) {
10643                 continue;
10644             }
10645             
10646             var size_cls = w[j].split("-");
10647             
10648             if(!Number.isInteger(size_cls[1] * 1)) {
10649                 continue;
10650             }
10651             
10652             if(!this.colModel.config[col_index][size_cls[0]]) {
10653                 continue;
10654             }
10655             
10656             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10657                 continue;
10658             }
10659             
10660             h_row[0].classList.replace(
10661                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10662                 "col-"+size_cls[0]+"-"+size_cls[1]
10663             );
10664             
10665             for(var i = 0; i < rows.length; i++) {
10666                 
10667                 var size_cls = w[j].split("-");
10668                 
10669                 if(!Number.isInteger(size_cls[1] * 1)) {
10670                     continue;
10671                 }
10672                 
10673                 if(!this.colModel.config[col_index][size_cls[0]]) {
10674                     continue;
10675                 }
10676                 
10677                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10678                     continue;
10679                 }
10680                 
10681                 rows[i].classList.replace(
10682                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10683                     "col-"+size_cls[0]+"-"+size_cls[1]
10684                 );
10685             }
10686             
10687             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10688         }
10689     }
10690 });
10691
10692 // currently only used to find the split on drag.. 
10693 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10694
10695 /**
10696  * @depricated
10697 */
10698 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10699 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10700 /*
10701  * - LGPL
10702  *
10703  * table cell
10704  * 
10705  */
10706
10707 /**
10708  * @class Roo.bootstrap.TableCell
10709  * @extends Roo.bootstrap.Component
10710  * @children Roo.bootstrap.Component
10711  * @parent Roo.bootstrap.TableRow
10712  * Bootstrap TableCell class
10713  * 
10714  * @cfg {String} html cell contain text
10715  * @cfg {String} cls cell class
10716  * @cfg {String} tag cell tag (td|th) default td
10717  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10718  * @cfg {String} align Aligns the content in a cell
10719  * @cfg {String} axis Categorizes cells
10720  * @cfg {String} bgcolor Specifies the background color of a cell
10721  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10722  * @cfg {Number} colspan Specifies the number of columns a cell should span
10723  * @cfg {String} headers Specifies one or more header cells a cell is related to
10724  * @cfg {Number} height Sets the height of a cell
10725  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10726  * @cfg {Number} rowspan Sets the number of rows a cell should span
10727  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10728  * @cfg {String} valign Vertical aligns the content in a cell
10729  * @cfg {Number} width Specifies the width of a cell
10730  * 
10731  * @constructor
10732  * Create a new TableCell
10733  * @param {Object} config The config object
10734  */
10735
10736 Roo.bootstrap.TableCell = function(config){
10737     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10738 };
10739
10740 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10741     
10742     html: false,
10743     cls: false,
10744     tag: false,
10745     abbr: false,
10746     align: false,
10747     axis: false,
10748     bgcolor: false,
10749     charoff: false,
10750     colspan: false,
10751     headers: false,
10752     height: false,
10753     nowrap: false,
10754     rowspan: false,
10755     scope: false,
10756     valign: false,
10757     width: false,
10758     
10759     
10760     getAutoCreate : function(){
10761         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10762         
10763         cfg = {
10764             tag: 'td'
10765         };
10766         
10767         if(this.tag){
10768             cfg.tag = this.tag;
10769         }
10770         
10771         if (this.html) {
10772             cfg.html=this.html
10773         }
10774         if (this.cls) {
10775             cfg.cls=this.cls
10776         }
10777         if (this.abbr) {
10778             cfg.abbr=this.abbr
10779         }
10780         if (this.align) {
10781             cfg.align=this.align
10782         }
10783         if (this.axis) {
10784             cfg.axis=this.axis
10785         }
10786         if (this.bgcolor) {
10787             cfg.bgcolor=this.bgcolor
10788         }
10789         if (this.charoff) {
10790             cfg.charoff=this.charoff
10791         }
10792         if (this.colspan) {
10793             cfg.colspan=this.colspan
10794         }
10795         if (this.headers) {
10796             cfg.headers=this.headers
10797         }
10798         if (this.height) {
10799             cfg.height=this.height
10800         }
10801         if (this.nowrap) {
10802             cfg.nowrap=this.nowrap
10803         }
10804         if (this.rowspan) {
10805             cfg.rowspan=this.rowspan
10806         }
10807         if (this.scope) {
10808             cfg.scope=this.scope
10809         }
10810         if (this.valign) {
10811             cfg.valign=this.valign
10812         }
10813         if (this.width) {
10814             cfg.width=this.width
10815         }
10816         
10817         
10818         return cfg;
10819     }
10820    
10821 });
10822
10823  
10824
10825  /*
10826  * - LGPL
10827  *
10828  * table row
10829  * 
10830  */
10831
10832 /**
10833  * @class Roo.bootstrap.TableRow
10834  * @extends Roo.bootstrap.Component
10835  * @children Roo.bootstrap.TableCell
10836  * @parent Roo.bootstrap.TableBody
10837  * Bootstrap TableRow class
10838  * @cfg {String} cls row class
10839  * @cfg {String} align Aligns the content in a table row
10840  * @cfg {String} bgcolor Specifies a background color for a table row
10841  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10842  * @cfg {String} valign Vertical aligns the content in a table row
10843  * 
10844  * @constructor
10845  * Create a new TableRow
10846  * @param {Object} config The config object
10847  */
10848
10849 Roo.bootstrap.TableRow = function(config){
10850     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10851 };
10852
10853 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10854     
10855     cls: false,
10856     align: false,
10857     bgcolor: false,
10858     charoff: false,
10859     valign: false,
10860     
10861     getAutoCreate : function(){
10862         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10863         
10864         cfg = {
10865             tag: 'tr'
10866         };
10867             
10868         if(this.cls){
10869             cfg.cls = this.cls;
10870         }
10871         if(this.align){
10872             cfg.align = this.align;
10873         }
10874         if(this.bgcolor){
10875             cfg.bgcolor = this.bgcolor;
10876         }
10877         if(this.charoff){
10878             cfg.charoff = this.charoff;
10879         }
10880         if(this.valign){
10881             cfg.valign = this.valign;
10882         }
10883         
10884         return cfg;
10885     }
10886    
10887 });
10888
10889  
10890
10891  /*
10892  * - LGPL
10893  *
10894  * table body
10895  * 
10896  */
10897
10898 /**
10899  * @class Roo.bootstrap.TableBody
10900  * @extends Roo.bootstrap.Component
10901  * @children Roo.bootstrap.TableRow
10902  * @parent Roo.bootstrap.Table
10903  * Bootstrap TableBody class
10904  * @cfg {String} cls element class
10905  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10906  * @cfg {String} align Aligns the content inside the element
10907  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10908  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10909  * 
10910  * @constructor
10911  * Create a new TableBody
10912  * @param {Object} config The config object
10913  */
10914
10915 Roo.bootstrap.TableBody = function(config){
10916     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10917 };
10918
10919 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10920     
10921     cls: false,
10922     tag: false,
10923     align: false,
10924     charoff: false,
10925     valign: false,
10926     
10927     getAutoCreate : function(){
10928         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10929         
10930         cfg = {
10931             tag: 'tbody'
10932         };
10933             
10934         if (this.cls) {
10935             cfg.cls=this.cls
10936         }
10937         if(this.tag){
10938             cfg.tag = this.tag;
10939         }
10940         
10941         if(this.align){
10942             cfg.align = this.align;
10943         }
10944         if(this.charoff){
10945             cfg.charoff = this.charoff;
10946         }
10947         if(this.valign){
10948             cfg.valign = this.valign;
10949         }
10950         
10951         return cfg;
10952     }
10953     
10954     
10955 //    initEvents : function()
10956 //    {
10957 //        
10958 //        if(!this.store){
10959 //            return;
10960 //        }
10961 //        
10962 //        this.store = Roo.factory(this.store, Roo.data);
10963 //        this.store.on('load', this.onLoad, this);
10964 //        
10965 //        this.store.load();
10966 //        
10967 //    },
10968 //    
10969 //    onLoad: function () 
10970 //    {   
10971 //        this.fireEvent('load', this);
10972 //    }
10973 //    
10974 //   
10975 });
10976
10977  
10978
10979  /*
10980  * Based on:
10981  * Ext JS Library 1.1.1
10982  * Copyright(c) 2006-2007, Ext JS, LLC.
10983  *
10984  * Originally Released Under LGPL - original licence link has changed is not relivant.
10985  *
10986  * Fork - LGPL
10987  * <script type="text/javascript">
10988  */
10989
10990 // as we use this in bootstrap.
10991 Roo.namespace('Roo.form');
10992  /**
10993  * @class Roo.form.Action
10994  * Internal Class used to handle form actions
10995  * @constructor
10996  * @param {Roo.form.BasicForm} el The form element or its id
10997  * @param {Object} config Configuration options
10998  */
10999
11000  
11001  
11002 // define the action interface
11003 Roo.form.Action = function(form, options){
11004     this.form = form;
11005     this.options = options || {};
11006 };
11007 /**
11008  * Client Validation Failed
11009  * @const 
11010  */
11011 Roo.form.Action.CLIENT_INVALID = 'client';
11012 /**
11013  * Server Validation Failed
11014  * @const 
11015  */
11016 Roo.form.Action.SERVER_INVALID = 'server';
11017  /**
11018  * Connect to Server Failed
11019  * @const 
11020  */
11021 Roo.form.Action.CONNECT_FAILURE = 'connect';
11022 /**
11023  * Reading Data from Server Failed
11024  * @const 
11025  */
11026 Roo.form.Action.LOAD_FAILURE = 'load';
11027
11028 Roo.form.Action.prototype = {
11029     type : 'default',
11030     failureType : undefined,
11031     response : undefined,
11032     result : undefined,
11033
11034     // interface method
11035     run : function(options){
11036
11037     },
11038
11039     // interface method
11040     success : function(response){
11041
11042     },
11043
11044     // interface method
11045     handleResponse : function(response){
11046
11047     },
11048
11049     // default connection failure
11050     failure : function(response){
11051         
11052         this.response = response;
11053         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11054         this.form.afterAction(this, false);
11055     },
11056
11057     processResponse : function(response){
11058         this.response = response;
11059         if(!response.responseText){
11060             return true;
11061         }
11062         this.result = this.handleResponse(response);
11063         return this.result;
11064     },
11065
11066     // utility functions used internally
11067     getUrl : function(appendParams){
11068         var url = this.options.url || this.form.url || this.form.el.dom.action;
11069         if(appendParams){
11070             var p = this.getParams();
11071             if(p){
11072                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11073             }
11074         }
11075         return url;
11076     },
11077
11078     getMethod : function(){
11079         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11080     },
11081
11082     getParams : function(){
11083         var bp = this.form.baseParams;
11084         var p = this.options.params;
11085         if(p){
11086             if(typeof p == "object"){
11087                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11088             }else if(typeof p == 'string' && bp){
11089                 p += '&' + Roo.urlEncode(bp);
11090             }
11091         }else if(bp){
11092             p = Roo.urlEncode(bp);
11093         }
11094         return p;
11095     },
11096
11097     createCallback : function(){
11098         return {
11099             success: this.success,
11100             failure: this.failure,
11101             scope: this,
11102             timeout: (this.form.timeout*1000),
11103             upload: this.form.fileUpload ? this.success : undefined
11104         };
11105     }
11106 };
11107
11108 Roo.form.Action.Submit = function(form, options){
11109     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11110 };
11111
11112 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11113     type : 'submit',
11114
11115     haveProgress : false,
11116     uploadComplete : false,
11117     
11118     // uploadProgress indicator.
11119     uploadProgress : function()
11120     {
11121         if (!this.form.progressUrl) {
11122             return;
11123         }
11124         
11125         if (!this.haveProgress) {
11126             Roo.MessageBox.progress("Uploading", "Uploading");
11127         }
11128         if (this.uploadComplete) {
11129            Roo.MessageBox.hide();
11130            return;
11131         }
11132         
11133         this.haveProgress = true;
11134    
11135         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11136         
11137         var c = new Roo.data.Connection();
11138         c.request({
11139             url : this.form.progressUrl,
11140             params: {
11141                 id : uid
11142             },
11143             method: 'GET',
11144             success : function(req){
11145                //console.log(data);
11146                 var rdata = false;
11147                 var edata;
11148                 try  {
11149                    rdata = Roo.decode(req.responseText)
11150                 } catch (e) {
11151                     Roo.log("Invalid data from server..");
11152                     Roo.log(edata);
11153                     return;
11154                 }
11155                 if (!rdata || !rdata.success) {
11156                     Roo.log(rdata);
11157                     Roo.MessageBox.alert(Roo.encode(rdata));
11158                     return;
11159                 }
11160                 var data = rdata.data;
11161                 
11162                 if (this.uploadComplete) {
11163                    Roo.MessageBox.hide();
11164                    return;
11165                 }
11166                    
11167                 if (data){
11168                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11169                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11170                     );
11171                 }
11172                 this.uploadProgress.defer(2000,this);
11173             },
11174        
11175             failure: function(data) {
11176                 Roo.log('progress url failed ');
11177                 Roo.log(data);
11178             },
11179             scope : this
11180         });
11181            
11182     },
11183     
11184     
11185     run : function()
11186     {
11187         // run get Values on the form, so it syncs any secondary forms.
11188         this.form.getValues();
11189         
11190         var o = this.options;
11191         var method = this.getMethod();
11192         var isPost = method == 'POST';
11193         if(o.clientValidation === false || this.form.isValid()){
11194             
11195             if (this.form.progressUrl) {
11196                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11197                     (new Date() * 1) + '' + Math.random());
11198                     
11199             } 
11200             
11201             
11202             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11203                 form:this.form.el.dom,
11204                 url:this.getUrl(!isPost),
11205                 method: method,
11206                 params:isPost ? this.getParams() : null,
11207                 isUpload: this.form.fileUpload,
11208                 formData : this.form.formData
11209             }));
11210             
11211             this.uploadProgress();
11212
11213         }else if (o.clientValidation !== false){ // client validation failed
11214             this.failureType = Roo.form.Action.CLIENT_INVALID;
11215             this.form.afterAction(this, false);
11216         }
11217     },
11218
11219     success : function(response)
11220     {
11221         this.uploadComplete= true;
11222         if (this.haveProgress) {
11223             Roo.MessageBox.hide();
11224         }
11225         
11226         
11227         var result = this.processResponse(response);
11228         if(result === true || result.success){
11229             this.form.afterAction(this, true);
11230             return;
11231         }
11232         if(result.errors){
11233             this.form.markInvalid(result.errors);
11234             this.failureType = Roo.form.Action.SERVER_INVALID;
11235         }
11236         this.form.afterAction(this, false);
11237     },
11238     failure : function(response)
11239     {
11240         this.uploadComplete= true;
11241         if (this.haveProgress) {
11242             Roo.MessageBox.hide();
11243         }
11244         
11245         this.response = response;
11246         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11247         this.form.afterAction(this, false);
11248     },
11249     
11250     handleResponse : function(response){
11251         if(this.form.errorReader){
11252             var rs = this.form.errorReader.read(response);
11253             var errors = [];
11254             if(rs.records){
11255                 for(var i = 0, len = rs.records.length; i < len; i++) {
11256                     var r = rs.records[i];
11257                     errors[i] = r.data;
11258                 }
11259             }
11260             if(errors.length < 1){
11261                 errors = null;
11262             }
11263             return {
11264                 success : rs.success,
11265                 errors : errors
11266             };
11267         }
11268         var ret = false;
11269         try {
11270             ret = Roo.decode(response.responseText);
11271         } catch (e) {
11272             ret = {
11273                 success: false,
11274                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11275                 errors : []
11276             };
11277         }
11278         return ret;
11279         
11280     }
11281 });
11282
11283
11284 Roo.form.Action.Load = function(form, options){
11285     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11286     this.reader = this.form.reader;
11287 };
11288
11289 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11290     type : 'load',
11291
11292     run : function(){
11293         
11294         Roo.Ajax.request(Roo.apply(
11295                 this.createCallback(), {
11296                     method:this.getMethod(),
11297                     url:this.getUrl(false),
11298                     params:this.getParams()
11299         }));
11300     },
11301
11302     success : function(response){
11303         
11304         var result = this.processResponse(response);
11305         if(result === true || !result.success || !result.data){
11306             this.failureType = Roo.form.Action.LOAD_FAILURE;
11307             this.form.afterAction(this, false);
11308             return;
11309         }
11310         this.form.clearInvalid();
11311         this.form.setValues(result.data);
11312         this.form.afterAction(this, true);
11313     },
11314
11315     handleResponse : function(response){
11316         if(this.form.reader){
11317             var rs = this.form.reader.read(response);
11318             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11319             return {
11320                 success : rs.success,
11321                 data : data
11322             };
11323         }
11324         return Roo.decode(response.responseText);
11325     }
11326 });
11327
11328 Roo.form.Action.ACTION_TYPES = {
11329     'load' : Roo.form.Action.Load,
11330     'submit' : Roo.form.Action.Submit
11331 };/*
11332  * - LGPL
11333  *
11334  * form
11335  *
11336  */
11337
11338 /**
11339  * @class Roo.bootstrap.form.Form
11340  * @extends Roo.bootstrap.Component
11341  * @children Roo.bootstrap.Component
11342  * Bootstrap Form class
11343  * @cfg {String} method  GET | POST (default POST)
11344  * @cfg {String} labelAlign top | left (default top)
11345  * @cfg {String} align left  | right - for navbars
11346  * @cfg {Boolean} loadMask load mask when submit (default true)
11347
11348  *
11349  * @constructor
11350  * Create a new Form
11351  * @param {Object} config The config object
11352  */
11353
11354
11355 Roo.bootstrap.form.Form = function(config){
11356     
11357     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11358     
11359     Roo.bootstrap.form.Form.popover.apply();
11360     
11361     this.addEvents({
11362         /**
11363          * @event clientvalidation
11364          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11365          * @param {Form} this
11366          * @param {Boolean} valid true if the form has passed client-side validation
11367          */
11368         clientvalidation: true,
11369         /**
11370          * @event beforeaction
11371          * Fires before any action is performed. Return false to cancel the action.
11372          * @param {Form} this
11373          * @param {Action} action The action to be performed
11374          */
11375         beforeaction: true,
11376         /**
11377          * @event actionfailed
11378          * Fires when an action fails.
11379          * @param {Form} this
11380          * @param {Action} action The action that failed
11381          */
11382         actionfailed : true,
11383         /**
11384          * @event actioncomplete
11385          * Fires when an action is completed.
11386          * @param {Form} this
11387          * @param {Action} action The action that completed
11388          */
11389         actioncomplete : true
11390     });
11391 };
11392
11393 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11394
11395      /**
11396      * @cfg {String} method
11397      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11398      */
11399     method : 'POST',
11400     /**
11401      * @cfg {String} url
11402      * The URL to use for form actions if one isn't supplied in the action options.
11403      */
11404     /**
11405      * @cfg {Boolean} fileUpload
11406      * Set to true if this form is a file upload.
11407      */
11408
11409     /**
11410      * @cfg {Object} baseParams
11411      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11412      */
11413
11414     /**
11415      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11416      */
11417     timeout: 30,
11418     /**
11419      * @cfg {Sting} align (left|right) for navbar forms
11420      */
11421     align : 'left',
11422
11423     // private
11424     activeAction : null,
11425
11426     /**
11427      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11428      * element by passing it or its id or mask the form itself by passing in true.
11429      * @type Mixed
11430      */
11431     waitMsgTarget : false,
11432
11433     loadMask : true,
11434     
11435     /**
11436      * @cfg {Boolean} errorMask (true|false) default false
11437      */
11438     errorMask : false,
11439     
11440     /**
11441      * @cfg {Number} maskOffset Default 100
11442      */
11443     maskOffset : 100,
11444     
11445     /**
11446      * @cfg {Boolean} maskBody
11447      */
11448     maskBody : false,
11449
11450     getAutoCreate : function(){
11451
11452         var cfg = {
11453             tag: 'form',
11454             method : this.method || 'POST',
11455             id : this.id || Roo.id(),
11456             cls : ''
11457         };
11458         if (this.parent().xtype.match(/^Nav/)) {
11459             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11460
11461         }
11462
11463         if (this.labelAlign == 'left' ) {
11464             cfg.cls += ' form-horizontal';
11465         }
11466
11467
11468         return cfg;
11469     },
11470     initEvents : function()
11471     {
11472         this.el.on('submit', this.onSubmit, this);
11473         // this was added as random key presses on the form where triggering form submit.
11474         this.el.on('keypress', function(e) {
11475             if (e.getCharCode() != 13) {
11476                 return true;
11477             }
11478             // we might need to allow it for textareas.. and some other items.
11479             // check e.getTarget().
11480
11481             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11482                 return true;
11483             }
11484
11485             Roo.log("keypress blocked");
11486
11487             e.preventDefault();
11488             return false;
11489         });
11490         
11491     },
11492     // private
11493     onSubmit : function(e){
11494         e.stopEvent();
11495     },
11496
11497      /**
11498      * Returns true if client-side validation on the form is successful.
11499      * @return Boolean
11500      */
11501     isValid : function(){
11502         var items = this.getItems();
11503         var valid = true;
11504         var target = false;
11505         
11506         items.each(function(f){
11507             
11508             if(f.validate()){
11509                 return;
11510             }
11511             
11512             Roo.log('invalid field: ' + f.name);
11513             
11514             valid = false;
11515
11516             if(!target && f.el.isVisible(true)){
11517                 target = f;
11518             }
11519            
11520         });
11521         
11522         if(this.errorMask && !valid){
11523             Roo.bootstrap.form.Form.popover.mask(this, target);
11524         }
11525         
11526         return valid;
11527     },
11528     
11529     /**
11530      * Returns true if any fields in this form have changed since their original load.
11531      * @return Boolean
11532      */
11533     isDirty : function(){
11534         var dirty = false;
11535         var items = this.getItems();
11536         items.each(function(f){
11537            if(f.isDirty()){
11538                dirty = true;
11539                return false;
11540            }
11541            return true;
11542         });
11543         return dirty;
11544     },
11545      /**
11546      * Performs a predefined action (submit or load) or custom actions you define on this form.
11547      * @param {String} actionName The name of the action type
11548      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11549      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11550      * accept other config options):
11551      * <pre>
11552 Property          Type             Description
11553 ----------------  ---------------  ----------------------------------------------------------------------------------
11554 url               String           The url for the action (defaults to the form's url)
11555 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11556 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11557 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11558                                    validate the form on the client (defaults to false)
11559      * </pre>
11560      * @return {BasicForm} this
11561      */
11562     doAction : function(action, options){
11563         if(typeof action == 'string'){
11564             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11565         }
11566         if(this.fireEvent('beforeaction', this, action) !== false){
11567             this.beforeAction(action);
11568             action.run.defer(100, action);
11569         }
11570         return this;
11571     },
11572
11573     // private
11574     beforeAction : function(action){
11575         var o = action.options;
11576         
11577         if(this.loadMask){
11578             
11579             if(this.maskBody){
11580                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11581             } else {
11582                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11583             }
11584         }
11585         // not really supported yet.. ??
11586
11587         //if(this.waitMsgTarget === true){
11588         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11589         //}else if(this.waitMsgTarget){
11590         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11591         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11592         //}else {
11593         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11594        // }
11595
11596     },
11597
11598     // private
11599     afterAction : function(action, success){
11600         this.activeAction = null;
11601         var o = action.options;
11602
11603         if(this.loadMask){
11604             
11605             if(this.maskBody){
11606                 Roo.get(document.body).unmask();
11607             } else {
11608                 this.el.unmask();
11609             }
11610         }
11611         
11612         //if(this.waitMsgTarget === true){
11613 //            this.el.unmask();
11614         //}else if(this.waitMsgTarget){
11615         //    this.waitMsgTarget.unmask();
11616         //}else{
11617         //    Roo.MessageBox.updateProgress(1);
11618         //    Roo.MessageBox.hide();
11619        // }
11620         //
11621         if(success){
11622             if(o.reset){
11623                 this.reset();
11624             }
11625             Roo.callback(o.success, o.scope, [this, action]);
11626             this.fireEvent('actioncomplete', this, action);
11627
11628         }else{
11629
11630             // failure condition..
11631             // we have a scenario where updates need confirming.
11632             // eg. if a locking scenario exists..
11633             // we look for { errors : { needs_confirm : true }} in the response.
11634             if (
11635                 (typeof(action.result) != 'undefined')  &&
11636                 (typeof(action.result.errors) != 'undefined')  &&
11637                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11638            ){
11639                 var _t = this;
11640                 Roo.log("not supported yet");
11641                  /*
11642
11643                 Roo.MessageBox.confirm(
11644                     "Change requires confirmation",
11645                     action.result.errorMsg,
11646                     function(r) {
11647                         if (r != 'yes') {
11648                             return;
11649                         }
11650                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11651                     }
11652
11653                 );
11654                 */
11655
11656
11657                 return;
11658             }
11659
11660             Roo.callback(o.failure, o.scope, [this, action]);
11661             // show an error message if no failed handler is set..
11662             if (!this.hasListener('actionfailed')) {
11663                 Roo.log("need to add dialog support");
11664                 /*
11665                 Roo.MessageBox.alert("Error",
11666                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11667                         action.result.errorMsg :
11668                         "Saving Failed, please check your entries or try again"
11669                 );
11670                 */
11671             }
11672
11673             this.fireEvent('actionfailed', this, action);
11674         }
11675
11676     },
11677     /**
11678      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11679      * @param {String} id The value to search for
11680      * @return Field
11681      */
11682     findField : function(id){
11683         var items = this.getItems();
11684         var field = items.get(id);
11685         if(!field){
11686              items.each(function(f){
11687                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11688                     field = f;
11689                     return false;
11690                 }
11691                 return true;
11692             });
11693         }
11694         return field || null;
11695     },
11696      /**
11697      * Mark fields in this form invalid in bulk.
11698      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11699      * @return {BasicForm} this
11700      */
11701     markInvalid : function(errors){
11702         if(errors instanceof Array){
11703             for(var i = 0, len = errors.length; i < len; i++){
11704                 var fieldError = errors[i];
11705                 var f = this.findField(fieldError.id);
11706                 if(f){
11707                     f.markInvalid(fieldError.msg);
11708                 }
11709             }
11710         }else{
11711             var field, id;
11712             for(id in errors){
11713                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11714                     field.markInvalid(errors[id]);
11715                 }
11716             }
11717         }
11718         //Roo.each(this.childForms || [], function (f) {
11719         //    f.markInvalid(errors);
11720         //});
11721
11722         return this;
11723     },
11724
11725     /**
11726      * Set values for fields in this form in bulk.
11727      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11728      * @return {BasicForm} this
11729      */
11730     setValues : function(values){
11731         if(values instanceof Array){ // array of objects
11732             for(var i = 0, len = values.length; i < len; i++){
11733                 var v = values[i];
11734                 var f = this.findField(v.id);
11735                 if(f){
11736                     f.setValue(v.value);
11737                     if(this.trackResetOnLoad){
11738                         f.originalValue = f.getValue();
11739                     }
11740                 }
11741             }
11742         }else{ // object hash
11743             var field, id;
11744             for(id in values){
11745                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11746
11747                     if (field.setFromData &&
11748                         field.valueField &&
11749                         field.displayField &&
11750                         // combos' with local stores can
11751                         // be queried via setValue()
11752                         // to set their value..
11753                         (field.store && !field.store.isLocal)
11754                         ) {
11755                         // it's a combo
11756                         var sd = { };
11757                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11758                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11759                         field.setFromData(sd);
11760
11761                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11762                         
11763                         field.setFromData(values);
11764                         
11765                     } else {
11766                         field.setValue(values[id]);
11767                     }
11768
11769
11770                     if(this.trackResetOnLoad){
11771                         field.originalValue = field.getValue();
11772                     }
11773                 }
11774             }
11775         }
11776
11777         //Roo.each(this.childForms || [], function (f) {
11778         //    f.setValues(values);
11779         //});
11780
11781         return this;
11782     },
11783
11784     /**
11785      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11786      * they are returned as an array.
11787      * @param {Boolean} asString
11788      * @return {Object}
11789      */
11790     getValues : function(asString){
11791         //if (this.childForms) {
11792             // copy values from the child forms
11793         //    Roo.each(this.childForms, function (f) {
11794         //        this.setValues(f.getValues());
11795         //    }, this);
11796         //}
11797
11798
11799
11800         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11801         if(asString === true){
11802             return fs;
11803         }
11804         return Roo.urlDecode(fs);
11805     },
11806
11807     /**
11808      * Returns the fields in this form as an object with key/value pairs.
11809      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11810      * @return {Object}
11811      */
11812     getFieldValues : function(with_hidden)
11813     {
11814         var items = this.getItems();
11815         var ret = {};
11816         items.each(function(f){
11817             
11818             if (!f.getName()) {
11819                 return;
11820             }
11821             
11822             var v = f.getValue();
11823             
11824             if (f.inputType =='radio') {
11825                 if (typeof(ret[f.getName()]) == 'undefined') {
11826                     ret[f.getName()] = ''; // empty..
11827                 }
11828
11829                 if (!f.el.dom.checked) {
11830                     return;
11831
11832                 }
11833                 v = f.el.dom.value;
11834
11835             }
11836             
11837             if(f.xtype == 'MoneyField'){
11838                 ret[f.currencyName] = f.getCurrency();
11839             }
11840
11841             // not sure if this supported any more..
11842             if ((typeof(v) == 'object') && f.getRawValue) {
11843                 v = f.getRawValue() ; // dates..
11844             }
11845             // combo boxes where name != hiddenName...
11846             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11847                 ret[f.name] = f.getRawValue();
11848             }
11849             ret[f.getName()] = v;
11850         });
11851
11852         return ret;
11853     },
11854
11855     /**
11856      * Clears all invalid messages in this form.
11857      * @return {BasicForm} this
11858      */
11859     clearInvalid : function(){
11860         var items = this.getItems();
11861
11862         items.each(function(f){
11863            f.clearInvalid();
11864         });
11865
11866         return this;
11867     },
11868
11869     /**
11870      * Resets this form.
11871      * @return {BasicForm} this
11872      */
11873     reset : function(){
11874         var items = this.getItems();
11875         items.each(function(f){
11876             f.reset();
11877         });
11878
11879         Roo.each(this.childForms || [], function (f) {
11880             f.reset();
11881         });
11882
11883
11884         return this;
11885     },
11886     
11887     getItems : function()
11888     {
11889         var r=new Roo.util.MixedCollection(false, function(o){
11890             return o.id || (o.id = Roo.id());
11891         });
11892         var iter = function(el) {
11893             if (el.inputEl) {
11894                 r.add(el);
11895             }
11896             if (!el.items) {
11897                 return;
11898             }
11899             Roo.each(el.items,function(e) {
11900                 iter(e);
11901             });
11902         };
11903
11904         iter(this);
11905         return r;
11906     },
11907     
11908     hideFields : function(items)
11909     {
11910         Roo.each(items, function(i){
11911             
11912             var f = this.findField(i);
11913             
11914             if(!f){
11915                 return;
11916             }
11917             
11918             f.hide();
11919             
11920         }, this);
11921     },
11922     
11923     showFields : function(items)
11924     {
11925         Roo.each(items, function(i){
11926             
11927             var f = this.findField(i);
11928             
11929             if(!f){
11930                 return;
11931             }
11932             
11933             f.show();
11934             
11935         }, this);
11936     }
11937
11938 });
11939
11940 Roo.apply(Roo.bootstrap.form.Form, {
11941     
11942     popover : {
11943         
11944         padding : 5,
11945         
11946         isApplied : false,
11947         
11948         isMasked : false,
11949         
11950         form : false,
11951         
11952         target : false,
11953         
11954         toolTip : false,
11955         
11956         intervalID : false,
11957         
11958         maskEl : false,
11959         
11960         apply : function()
11961         {
11962             if(this.isApplied){
11963                 return;
11964             }
11965             
11966             this.maskEl = {
11967                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11968                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11969                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11970                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11971             };
11972             
11973             this.maskEl.top.enableDisplayMode("block");
11974             this.maskEl.left.enableDisplayMode("block");
11975             this.maskEl.bottom.enableDisplayMode("block");
11976             this.maskEl.right.enableDisplayMode("block");
11977             
11978             this.toolTip = new Roo.bootstrap.Tooltip({
11979                 cls : 'roo-form-error-popover',
11980                 alignment : {
11981                     'left' : ['r-l', [-2,0], 'right'],
11982                     'right' : ['l-r', [2,0], 'left'],
11983                     'bottom' : ['tl-bl', [0,2], 'top'],
11984                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11985                 }
11986             });
11987             
11988             this.toolTip.render(Roo.get(document.body));
11989
11990             this.toolTip.el.enableDisplayMode("block");
11991             
11992             Roo.get(document.body).on('click', function(){
11993                 this.unmask();
11994             }, this);
11995             
11996             Roo.get(document.body).on('touchstart', function(){
11997                 this.unmask();
11998             }, this);
11999             
12000             this.isApplied = true
12001         },
12002         
12003         mask : function(form, target)
12004         {
12005             this.form = form;
12006             
12007             this.target = target;
12008             
12009             if(!this.form.errorMask || !target.el){
12010                 return;
12011             }
12012             
12013             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12014             
12015             Roo.log(scrollable);
12016             
12017             var ot = this.target.el.calcOffsetsTo(scrollable);
12018             
12019             var scrollTo = ot[1] - this.form.maskOffset;
12020             
12021             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12022             
12023             scrollable.scrollTo('top', scrollTo);
12024             
12025             var box = this.target.el.getBox();
12026             Roo.log(box);
12027             var zIndex = Roo.bootstrap.Modal.zIndex++;
12028
12029             
12030             this.maskEl.top.setStyle('position', 'absolute');
12031             this.maskEl.top.setStyle('z-index', zIndex);
12032             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12033             this.maskEl.top.setLeft(0);
12034             this.maskEl.top.setTop(0);
12035             this.maskEl.top.show();
12036             
12037             this.maskEl.left.setStyle('position', 'absolute');
12038             this.maskEl.left.setStyle('z-index', zIndex);
12039             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12040             this.maskEl.left.setLeft(0);
12041             this.maskEl.left.setTop(box.y - this.padding);
12042             this.maskEl.left.show();
12043
12044             this.maskEl.bottom.setStyle('position', 'absolute');
12045             this.maskEl.bottom.setStyle('z-index', zIndex);
12046             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12047             this.maskEl.bottom.setLeft(0);
12048             this.maskEl.bottom.setTop(box.bottom + this.padding);
12049             this.maskEl.bottom.show();
12050
12051             this.maskEl.right.setStyle('position', 'absolute');
12052             this.maskEl.right.setStyle('z-index', zIndex);
12053             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12054             this.maskEl.right.setLeft(box.right + this.padding);
12055             this.maskEl.right.setTop(box.y - this.padding);
12056             this.maskEl.right.show();
12057
12058             this.toolTip.bindEl = this.target.el;
12059
12060             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12061
12062             var tip = this.target.blankText;
12063
12064             if(this.target.getValue() !== '' ) {
12065                 
12066                 if (this.target.invalidText.length) {
12067                     tip = this.target.invalidText;
12068                 } else if (this.target.regexText.length){
12069                     tip = this.target.regexText;
12070                 }
12071             }
12072
12073             this.toolTip.show(tip);
12074
12075             this.intervalID = window.setInterval(function() {
12076                 Roo.bootstrap.form.Form.popover.unmask();
12077             }, 10000);
12078
12079             window.onwheel = function(){ return false;};
12080             
12081             (function(){ this.isMasked = true; }).defer(500, this);
12082             
12083         },
12084         
12085         unmask : function()
12086         {
12087             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12088                 return;
12089             }
12090             
12091             this.maskEl.top.setStyle('position', 'absolute');
12092             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12093             this.maskEl.top.hide();
12094
12095             this.maskEl.left.setStyle('position', 'absolute');
12096             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12097             this.maskEl.left.hide();
12098
12099             this.maskEl.bottom.setStyle('position', 'absolute');
12100             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12101             this.maskEl.bottom.hide();
12102
12103             this.maskEl.right.setStyle('position', 'absolute');
12104             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12105             this.maskEl.right.hide();
12106             
12107             this.toolTip.hide();
12108             
12109             this.toolTip.el.hide();
12110             
12111             window.onwheel = function(){ return true;};
12112             
12113             if(this.intervalID){
12114                 window.clearInterval(this.intervalID);
12115                 this.intervalID = false;
12116             }
12117             
12118             this.isMasked = false;
12119             
12120         }
12121         
12122     }
12123     
12124 });
12125
12126 /*
12127  * Based on:
12128  * Ext JS Library 1.1.1
12129  * Copyright(c) 2006-2007, Ext JS, LLC.
12130  *
12131  * Originally Released Under LGPL - original licence link has changed is not relivant.
12132  *
12133  * Fork - LGPL
12134  * <script type="text/javascript">
12135  */
12136 /**
12137  * @class Roo.form.VTypes
12138  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12139  * @static
12140  */
12141 Roo.form.VTypes = function(){
12142     // closure these in so they are only created once.
12143     var alpha = /^[a-zA-Z_]+$/;
12144     var alphanum = /^[a-zA-Z0-9_]+$/;
12145     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12146     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12147
12148     // All these messages and functions are configurable
12149     return {
12150         /**
12151          * The function used to validate email addresses
12152          * @param {String} value The email address
12153          */
12154         'email' : function(v){
12155             return email.test(v);
12156         },
12157         /**
12158          * The error text to display when the email validation function returns false
12159          * @type String
12160          */
12161         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12162         /**
12163          * The keystroke filter mask to be applied on email input
12164          * @type RegExp
12165          */
12166         'emailMask' : /[a-z0-9_\.\-@]/i,
12167
12168         /**
12169          * The function used to validate URLs
12170          * @param {String} value The URL
12171          */
12172         'url' : function(v){
12173             return url.test(v);
12174         },
12175         /**
12176          * The error text to display when the url validation function returns false
12177          * @type String
12178          */
12179         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12180         
12181         /**
12182          * The function used to validate alpha values
12183          * @param {String} value The value
12184          */
12185         'alpha' : function(v){
12186             return alpha.test(v);
12187         },
12188         /**
12189          * The error text to display when the alpha validation function returns false
12190          * @type String
12191          */
12192         'alphaText' : 'This field should only contain letters and _',
12193         /**
12194          * The keystroke filter mask to be applied on alpha input
12195          * @type RegExp
12196          */
12197         'alphaMask' : /[a-z_]/i,
12198
12199         /**
12200          * The function used to validate alphanumeric values
12201          * @param {String} value The value
12202          */
12203         'alphanum' : function(v){
12204             return alphanum.test(v);
12205         },
12206         /**
12207          * The error text to display when the alphanumeric validation function returns false
12208          * @type String
12209          */
12210         'alphanumText' : 'This field should only contain letters, numbers and _',
12211         /**
12212          * The keystroke filter mask to be applied on alphanumeric input
12213          * @type RegExp
12214          */
12215         'alphanumMask' : /[a-z0-9_]/i
12216     };
12217 }();/*
12218  * - LGPL
12219  *
12220  * Input
12221  * 
12222  */
12223
12224 /**
12225  * @class Roo.bootstrap.form.Input
12226  * @extends Roo.bootstrap.Component
12227  * Bootstrap Input class
12228  * @cfg {Boolean} disabled is it disabled
12229  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12230  * @cfg {String} name name of the input
12231  * @cfg {string} fieldLabel - the label associated
12232  * @cfg {string} placeholder - placeholder to put in text.
12233  * @cfg {string} before - input group add on before
12234  * @cfg {string} after - input group add on after
12235  * @cfg {string} size - (lg|sm) or leave empty..
12236  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12237  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12238  * @cfg {Number} md colspan out of 12 for computer-sized screens
12239  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12240  * @cfg {string} value default value of the input
12241  * @cfg {Number} labelWidth set the width of label 
12242  * @cfg {Number} labellg set the width of label (1-12)
12243  * @cfg {Number} labelmd set the width of label (1-12)
12244  * @cfg {Number} labelsm set the width of label (1-12)
12245  * @cfg {Number} labelxs set the width of label (1-12)
12246  * @cfg {String} labelAlign (top|left)
12247  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12248  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12249  * @cfg {String} indicatorpos (left|right) default left
12250  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12251  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12252  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12253  * @cfg {Roo.bootstrap.Button} before Button to show before
12254  * @cfg {Roo.bootstrap.Button} afterButton to show before
12255  * @cfg {String} align (left|center|right) Default left
12256  * @cfg {Boolean} forceFeedback (true|false) Default false
12257  * 
12258  * @constructor
12259  * Create a new Input
12260  * @param {Object} config The config object
12261  */
12262
12263 Roo.bootstrap.form.Input = function(config){
12264     
12265     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12266     
12267     this.addEvents({
12268         /**
12269          * @event focus
12270          * Fires when this field receives input focus.
12271          * @param {Roo.form.Field} this
12272          */
12273         focus : true,
12274         /**
12275          * @event blur
12276          * Fires when this field loses input focus.
12277          * @param {Roo.form.Field} this
12278          */
12279         blur : true,
12280         /**
12281          * @event specialkey
12282          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12283          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12284          * @param {Roo.form.Field} this
12285          * @param {Roo.EventObject} e The event object
12286          */
12287         specialkey : true,
12288         /**
12289          * @event change
12290          * Fires just before the field blurs if the field value has changed.
12291          * @param {Roo.form.Field} this
12292          * @param {Mixed} newValue The new value
12293          * @param {Mixed} oldValue The original value
12294          */
12295         change : true,
12296         /**
12297          * @event invalid
12298          * Fires after the field has been marked as invalid.
12299          * @param {Roo.form.Field} this
12300          * @param {String} msg The validation message
12301          */
12302         invalid : true,
12303         /**
12304          * @event valid
12305          * Fires after the field has been validated with no errors.
12306          * @param {Roo.form.Field} this
12307          */
12308         valid : true,
12309          /**
12310          * @event keyup
12311          * Fires after the key up
12312          * @param {Roo.form.Field} this
12313          * @param {Roo.EventObject}  e The event Object
12314          */
12315         keyup : true,
12316         /**
12317          * @event paste
12318          * Fires after the user pastes into input
12319          * @param {Roo.form.Field} this
12320          * @param {Roo.EventObject}  e The event Object
12321          */
12322         paste : true
12323     });
12324 };
12325
12326 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12327      /**
12328      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12329       automatic validation (defaults to "keyup").
12330      */
12331     validationEvent : "keyup",
12332      /**
12333      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12334      */
12335     validateOnBlur : true,
12336     /**
12337      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12338      */
12339     validationDelay : 250,
12340      /**
12341      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12342      */
12343     focusClass : "x-form-focus",  // not needed???
12344     
12345        
12346     /**
12347      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12348      */
12349     invalidClass : "has-warning",
12350     
12351     /**
12352      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12353      */
12354     validClass : "has-success",
12355     
12356     /**
12357      * @cfg {Boolean} hasFeedback (true|false) default true
12358      */
12359     hasFeedback : true,
12360     
12361     /**
12362      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12363      */
12364     invalidFeedbackClass : "glyphicon-warning-sign",
12365     
12366     /**
12367      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12368      */
12369     validFeedbackClass : "glyphicon-ok",
12370     
12371     /**
12372      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12373      */
12374     selectOnFocus : false,
12375     
12376      /**
12377      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12378      */
12379     maskRe : null,
12380        /**
12381      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12382      */
12383     vtype : null,
12384     
12385       /**
12386      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12387      */
12388     disableKeyFilter : false,
12389     
12390        /**
12391      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12392      */
12393     disabled : false,
12394      /**
12395      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12396      */
12397     allowBlank : true,
12398     /**
12399      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12400      */
12401     blankText : "Please complete this mandatory field",
12402     
12403      /**
12404      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12405      */
12406     minLength : 0,
12407     /**
12408      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12409      */
12410     maxLength : Number.MAX_VALUE,
12411     /**
12412      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12413      */
12414     minLengthText : "The minimum length for this field is {0}",
12415     /**
12416      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12417      */
12418     maxLengthText : "The maximum length for this field is {0}",
12419   
12420     
12421     /**
12422      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12423      * If available, this function will be called only after the basic validators all return true, and will be passed the
12424      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12425      */
12426     validator : null,
12427     /**
12428      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12429      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12430      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12431      */
12432     regex : null,
12433     /**
12434      * @cfg {String} regexText -- Depricated - use Invalid Text
12435      */
12436     regexText : "",
12437     
12438     /**
12439      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12440      */
12441     invalidText : "",
12442     
12443     
12444     
12445     autocomplete: false,
12446     
12447     
12448     fieldLabel : '',
12449     inputType : 'text',
12450     
12451     name : false,
12452     placeholder: false,
12453     before : false,
12454     after : false,
12455     size : false,
12456     hasFocus : false,
12457     preventMark: false,
12458     isFormField : true,
12459     value : '',
12460     labelWidth : 2,
12461     labelAlign : false,
12462     readOnly : false,
12463     align : false,
12464     formatedValue : false,
12465     forceFeedback : false,
12466     
12467     indicatorpos : 'left',
12468     
12469     labellg : 0,
12470     labelmd : 0,
12471     labelsm : 0,
12472     labelxs : 0,
12473     
12474     capture : '',
12475     accept : '',
12476     
12477     parentLabelAlign : function()
12478     {
12479         var parent = this;
12480         while (parent.parent()) {
12481             parent = parent.parent();
12482             if (typeof(parent.labelAlign) !='undefined') {
12483                 return parent.labelAlign;
12484             }
12485         }
12486         return 'left';
12487         
12488     },
12489     
12490     getAutoCreate : function()
12491     {
12492         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12493         
12494         var id = Roo.id();
12495         
12496         var cfg = {};
12497         
12498         if(this.inputType != 'hidden'){
12499             cfg.cls = 'form-group' //input-group
12500         }
12501         
12502         var input =  {
12503             tag: 'input',
12504             id : id,
12505             type : this.inputType,
12506             value : this.value,
12507             cls : 'form-control',
12508             placeholder : this.placeholder || '',
12509             autocomplete : this.autocomplete || 'new-password'
12510         };
12511         if (this.inputType == 'file') {
12512             input.style = 'overflow:hidden'; // why not in CSS?
12513         }
12514         
12515         if(this.capture.length){
12516             input.capture = this.capture;
12517         }
12518         
12519         if(this.accept.length){
12520             input.accept = this.accept + "/*";
12521         }
12522         
12523         if(this.align){
12524             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12525         }
12526         
12527         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12528             input.maxLength = this.maxLength;
12529         }
12530         
12531         if (this.disabled) {
12532             input.disabled=true;
12533         }
12534         
12535         if (this.readOnly) {
12536             input.readonly=true;
12537         }
12538         
12539         if (this.name) {
12540             input.name = this.name;
12541         }
12542         
12543         if (this.size) {
12544             input.cls += ' input-' + this.size;
12545         }
12546         
12547         var settings=this;
12548         ['xs','sm','md','lg'].map(function(size){
12549             if (settings[size]) {
12550                 cfg.cls += ' col-' + size + '-' + settings[size];
12551             }
12552         });
12553         
12554         var inputblock = input;
12555         
12556         var feedback = {
12557             tag: 'span',
12558             cls: 'glyphicon form-control-feedback'
12559         };
12560             
12561         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12562             
12563             inputblock = {
12564                 cls : 'has-feedback',
12565                 cn :  [
12566                     input,
12567                     feedback
12568                 ] 
12569             };  
12570         }
12571         
12572         if (this.before || this.after) {
12573             
12574             inputblock = {
12575                 cls : 'input-group',
12576                 cn :  [] 
12577             };
12578             
12579             if (this.before && typeof(this.before) == 'string') {
12580                 
12581                 inputblock.cn.push({
12582                     tag :'span',
12583                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12584                     html : this.before
12585                 });
12586             }
12587             if (this.before && typeof(this.before) == 'object') {
12588                 this.before = Roo.factory(this.before);
12589                 
12590                 inputblock.cn.push({
12591                     tag :'span',
12592                     cls : 'roo-input-before input-group-prepend   input-group-' +
12593                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12594                 });
12595             }
12596             
12597             inputblock.cn.push(input);
12598             
12599             if (this.after && typeof(this.after) == 'string') {
12600                 inputblock.cn.push({
12601                     tag :'span',
12602                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12603                     html : this.after
12604                 });
12605             }
12606             if (this.after && typeof(this.after) == 'object') {
12607                 this.after = Roo.factory(this.after);
12608                 
12609                 inputblock.cn.push({
12610                     tag :'span',
12611                     cls : 'roo-input-after input-group-append  input-group-' +
12612                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12613                 });
12614             }
12615             
12616             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12617                 inputblock.cls += ' has-feedback';
12618                 inputblock.cn.push(feedback);
12619             }
12620         };
12621         var indicator = {
12622             tag : 'i',
12623             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12624             tooltip : 'This field is required'
12625         };
12626         if (this.allowBlank ) {
12627             indicator.style = this.allowBlank ? ' display:none' : '';
12628         }
12629         if (align ==='left' && this.fieldLabel.length) {
12630             
12631             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12632             
12633             cfg.cn = [
12634                 indicator,
12635                 {
12636                     tag: 'label',
12637                     'for' :  id,
12638                     cls : 'control-label col-form-label',
12639                     html : this.fieldLabel
12640
12641                 },
12642                 {
12643                     cls : "", 
12644                     cn: [
12645                         inputblock
12646                     ]
12647                 }
12648             ];
12649             
12650             var labelCfg = cfg.cn[1];
12651             var contentCfg = cfg.cn[2];
12652             
12653             if(this.indicatorpos == 'right'){
12654                 cfg.cn = [
12655                     {
12656                         tag: 'label',
12657                         'for' :  id,
12658                         cls : 'control-label col-form-label',
12659                         cn : [
12660                             {
12661                                 tag : 'span',
12662                                 html : this.fieldLabel
12663                             },
12664                             indicator
12665                         ]
12666                     },
12667                     {
12668                         cls : "",
12669                         cn: [
12670                             inputblock
12671                         ]
12672                     }
12673
12674                 ];
12675                 
12676                 labelCfg = cfg.cn[0];
12677                 contentCfg = cfg.cn[1];
12678             
12679             }
12680             
12681             if(this.labelWidth > 12){
12682                 labelCfg.style = "width: " + this.labelWidth + 'px';
12683             }
12684             
12685             if(this.labelWidth < 13 && this.labelmd == 0){
12686                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12687             }
12688             
12689             if(this.labellg > 0){
12690                 labelCfg.cls += ' col-lg-' + this.labellg;
12691                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12692             }
12693             
12694             if(this.labelmd > 0){
12695                 labelCfg.cls += ' col-md-' + this.labelmd;
12696                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12697             }
12698             
12699             if(this.labelsm > 0){
12700                 labelCfg.cls += ' col-sm-' + this.labelsm;
12701                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12702             }
12703             
12704             if(this.labelxs > 0){
12705                 labelCfg.cls += ' col-xs-' + this.labelxs;
12706                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12707             }
12708             
12709             
12710         } else if ( this.fieldLabel.length) {
12711                 
12712             
12713             
12714             cfg.cn = [
12715                 {
12716                     tag : 'i',
12717                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12718                     tooltip : 'This field is required',
12719                     style : this.allowBlank ? ' display:none' : '' 
12720                 },
12721                 {
12722                     tag: 'label',
12723                    //cls : 'input-group-addon',
12724                     html : this.fieldLabel
12725
12726                 },
12727
12728                inputblock
12729
12730            ];
12731            
12732            if(this.indicatorpos == 'right'){
12733        
12734                 cfg.cn = [
12735                     {
12736                         tag: 'label',
12737                        //cls : 'input-group-addon',
12738                         html : this.fieldLabel
12739
12740                     },
12741                     {
12742                         tag : 'i',
12743                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12744                         tooltip : 'This field is required',
12745                         style : this.allowBlank ? ' display:none' : '' 
12746                     },
12747
12748                    inputblock
12749
12750                ];
12751
12752             }
12753
12754         } else {
12755             
12756             cfg.cn = [
12757
12758                     inputblock
12759
12760             ];
12761                 
12762                 
12763         };
12764         
12765         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12766            cfg.cls += ' navbar-form';
12767         }
12768         
12769         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12770             // on BS4 we do this only if not form 
12771             cfg.cls += ' navbar-form';
12772             cfg.tag = 'li';
12773         }
12774         
12775         return cfg;
12776         
12777     },
12778     /**
12779      * return the real input element.
12780      */
12781     inputEl: function ()
12782     {
12783         return this.el.select('input.form-control',true).first();
12784     },
12785     
12786     tooltipEl : function()
12787     {
12788         return this.inputEl();
12789     },
12790     
12791     indicatorEl : function()
12792     {
12793         if (Roo.bootstrap.version == 4) {
12794             return false; // not enabled in v4 yet.
12795         }
12796         
12797         var indicator = this.el.select('i.roo-required-indicator',true).first();
12798         
12799         if(!indicator){
12800             return false;
12801         }
12802         
12803         return indicator;
12804         
12805     },
12806     
12807     setDisabled : function(v)
12808     {
12809         var i  = this.inputEl().dom;
12810         if (!v) {
12811             i.removeAttribute('disabled');
12812             return;
12813             
12814         }
12815         i.setAttribute('disabled','true');
12816     },
12817     initEvents : function()
12818     {
12819           
12820         this.inputEl().on("keydown" , this.fireKey,  this);
12821         this.inputEl().on("focus", this.onFocus,  this);
12822         this.inputEl().on("blur", this.onBlur,  this);
12823         
12824         this.inputEl().relayEvent('keyup', this);
12825         this.inputEl().relayEvent('paste', this);
12826         
12827         this.indicator = this.indicatorEl();
12828         
12829         if(this.indicator){
12830             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12831         }
12832  
12833         // reference to original value for reset
12834         this.originalValue = this.getValue();
12835         //Roo.form.TextField.superclass.initEvents.call(this);
12836         if(this.validationEvent == 'keyup'){
12837             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12838             this.inputEl().on('keyup', this.filterValidation, this);
12839         }
12840         else if(this.validationEvent !== false){
12841             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12842         }
12843         
12844         if(this.selectOnFocus){
12845             this.on("focus", this.preFocus, this);
12846             
12847         }
12848         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12849             this.inputEl().on("keypress", this.filterKeys, this);
12850         } else {
12851             this.inputEl().relayEvent('keypress', this);
12852         }
12853        /* if(this.grow){
12854             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12855             this.el.on("click", this.autoSize,  this);
12856         }
12857         */
12858         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12859             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12860         }
12861         
12862         if (typeof(this.before) == 'object') {
12863             this.before.render(this.el.select('.roo-input-before',true).first());
12864         }
12865         if (typeof(this.after) == 'object') {
12866             this.after.render(this.el.select('.roo-input-after',true).first());
12867         }
12868         
12869         this.inputEl().on('change', this.onChange, this);
12870         
12871     },
12872     filterValidation : function(e){
12873         if(!e.isNavKeyPress()){
12874             this.validationTask.delay(this.validationDelay);
12875         }
12876     },
12877      /**
12878      * Validates the field value
12879      * @return {Boolean} True if the value is valid, else false
12880      */
12881     validate : function(){
12882         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12883         if(this.disabled || this.validateValue(this.getRawValue())){
12884             this.markValid();
12885             return true;
12886         }
12887         
12888         this.markInvalid();
12889         return false;
12890     },
12891     
12892     
12893     /**
12894      * Validates a value according to the field's validation rules and marks the field as invalid
12895      * if the validation fails
12896      * @param {Mixed} value The value to validate
12897      * @return {Boolean} True if the value is valid, else false
12898      */
12899     validateValue : function(value)
12900     {
12901         if(this.getVisibilityEl().hasClass('hidden')){
12902             return true;
12903         }
12904         
12905         if(value.length < 1)  { // if it's blank
12906             if(this.allowBlank){
12907                 return true;
12908             }
12909             return false;
12910         }
12911         
12912         if(value.length < this.minLength){
12913             return false;
12914         }
12915         if(value.length > this.maxLength){
12916             return false;
12917         }
12918         if(this.vtype){
12919             var vt = Roo.form.VTypes;
12920             if(!vt[this.vtype](value, this)){
12921                 return false;
12922             }
12923         }
12924         if(typeof this.validator == "function"){
12925             var msg = this.validator(value);
12926             if(msg !== true){
12927                 return false;
12928             }
12929             if (typeof(msg) == 'string') {
12930                 this.invalidText = msg;
12931             }
12932         }
12933         
12934         if(this.regex && !this.regex.test(value)){
12935             return false;
12936         }
12937         
12938         return true;
12939     },
12940     
12941      // private
12942     fireKey : function(e){
12943         //Roo.log('field ' + e.getKey());
12944         if(e.isNavKeyPress()){
12945             this.fireEvent("specialkey", this, e);
12946         }
12947     },
12948     focus : function (selectText){
12949         if(this.rendered){
12950             this.inputEl().focus();
12951             if(selectText === true){
12952                 this.inputEl().dom.select();
12953             }
12954         }
12955         return this;
12956     } ,
12957     
12958     onFocus : function(){
12959         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12960            // this.el.addClass(this.focusClass);
12961         }
12962         if(!this.hasFocus){
12963             this.hasFocus = true;
12964             this.startValue = this.getValue();
12965             this.fireEvent("focus", this);
12966         }
12967     },
12968     
12969     beforeBlur : Roo.emptyFn,
12970
12971     
12972     // private
12973     onBlur : function(){
12974         this.beforeBlur();
12975         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12976             //this.el.removeClass(this.focusClass);
12977         }
12978         this.hasFocus = false;
12979         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12980             this.validate();
12981         }
12982         var v = this.getValue();
12983         if(String(v) !== String(this.startValue)){
12984             this.fireEvent('change', this, v, this.startValue);
12985         }
12986         this.fireEvent("blur", this);
12987     },
12988     
12989     onChange : function(e)
12990     {
12991         var v = this.getValue();
12992         if(String(v) !== String(this.startValue)){
12993             this.fireEvent('change', this, v, this.startValue);
12994         }
12995         
12996     },
12997     
12998     /**
12999      * Resets the current field value to the originally loaded value and clears any validation messages
13000      */
13001     reset : function(){
13002         this.setValue(this.originalValue);
13003         this.validate();
13004     },
13005      /**
13006      * Returns the name of the field
13007      * @return {Mixed} name The name field
13008      */
13009     getName: function(){
13010         return this.name;
13011     },
13012      /**
13013      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13014      * @return {Mixed} value The field value
13015      */
13016     getValue : function(){
13017         
13018         var v = this.inputEl().getValue();
13019         
13020         return v;
13021     },
13022     /**
13023      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13024      * @return {Mixed} value The field value
13025      */
13026     getRawValue : function(){
13027         var v = this.inputEl().getValue();
13028         
13029         return v;
13030     },
13031     
13032     /**
13033      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13034      * @param {Mixed} value The value to set
13035      */
13036     setRawValue : function(v){
13037         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13038     },
13039     
13040     selectText : function(start, end){
13041         var v = this.getRawValue();
13042         if(v.length > 0){
13043             start = start === undefined ? 0 : start;
13044             end = end === undefined ? v.length : end;
13045             var d = this.inputEl().dom;
13046             if(d.setSelectionRange){
13047                 d.setSelectionRange(start, end);
13048             }else if(d.createTextRange){
13049                 var range = d.createTextRange();
13050                 range.moveStart("character", start);
13051                 range.moveEnd("character", v.length-end);
13052                 range.select();
13053             }
13054         }
13055     },
13056     
13057     /**
13058      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13059      * @param {Mixed} value The value to set
13060      */
13061     setValue : function(v){
13062         this.value = v;
13063         if(this.rendered){
13064             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13065             this.validate();
13066         }
13067     },
13068     
13069     /*
13070     processValue : function(value){
13071         if(this.stripCharsRe){
13072             var newValue = value.replace(this.stripCharsRe, '');
13073             if(newValue !== value){
13074                 this.setRawValue(newValue);
13075                 return newValue;
13076             }
13077         }
13078         return value;
13079     },
13080   */
13081     preFocus : function(){
13082         
13083         if(this.selectOnFocus){
13084             this.inputEl().dom.select();
13085         }
13086     },
13087     filterKeys : function(e){
13088         var k = e.getKey();
13089         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13090             return;
13091         }
13092         var c = e.getCharCode(), cc = String.fromCharCode(c);
13093         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13094             return;
13095         }
13096         if(!this.maskRe.test(cc)){
13097             e.stopEvent();
13098         }
13099     },
13100      /**
13101      * Clear any invalid styles/messages for this field
13102      */
13103     clearInvalid : function(){
13104         
13105         if(!this.el || this.preventMark){ // not rendered
13106             return;
13107         }
13108         
13109         
13110         this.el.removeClass([this.invalidClass, 'is-invalid']);
13111         
13112         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13113             
13114             var feedback = this.el.select('.form-control-feedback', true).first();
13115             
13116             if(feedback){
13117                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13118             }
13119             
13120         }
13121         
13122         if(this.indicator){
13123             this.indicator.removeClass('visible');
13124             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13125         }
13126         
13127         this.fireEvent('valid', this);
13128     },
13129     
13130      /**
13131      * Mark this field as valid
13132      */
13133     markValid : function()
13134     {
13135         if(!this.el  || this.preventMark){ // not rendered...
13136             return;
13137         }
13138         
13139         this.el.removeClass([this.invalidClass, this.validClass]);
13140         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13141
13142         var feedback = this.el.select('.form-control-feedback', true).first();
13143             
13144         if(feedback){
13145             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13146         }
13147         
13148         if(this.indicator){
13149             this.indicator.removeClass('visible');
13150             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13151         }
13152         
13153         if(this.disabled){
13154             return;
13155         }
13156         
13157            
13158         if(this.allowBlank && !this.getRawValue().length){
13159             return;
13160         }
13161         if (Roo.bootstrap.version == 3) {
13162             this.el.addClass(this.validClass);
13163         } else {
13164             this.inputEl().addClass('is-valid');
13165         }
13166
13167         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13168             
13169             var feedback = this.el.select('.form-control-feedback', true).first();
13170             
13171             if(feedback){
13172                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13173                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13174             }
13175             
13176         }
13177         
13178         this.fireEvent('valid', this);
13179     },
13180     
13181      /**
13182      * Mark this field as invalid
13183      * @param {String} msg The validation message
13184      */
13185     markInvalid : function(msg)
13186     {
13187         if(!this.el  || this.preventMark){ // not rendered
13188             return;
13189         }
13190         
13191         this.el.removeClass([this.invalidClass, this.validClass]);
13192         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13193         
13194         var feedback = this.el.select('.form-control-feedback', true).first();
13195             
13196         if(feedback){
13197             this.el.select('.form-control-feedback', true).first().removeClass(
13198                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13199         }
13200
13201         if(this.disabled){
13202             return;
13203         }
13204         
13205         if(this.allowBlank && !this.getRawValue().length){
13206             return;
13207         }
13208         
13209         if(this.indicator){
13210             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13211             this.indicator.addClass('visible');
13212         }
13213         if (Roo.bootstrap.version == 3) {
13214             this.el.addClass(this.invalidClass);
13215         } else {
13216             this.inputEl().addClass('is-invalid');
13217         }
13218         
13219         
13220         
13221         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13222             
13223             var feedback = this.el.select('.form-control-feedback', true).first();
13224             
13225             if(feedback){
13226                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13227                 
13228                 if(this.getValue().length || this.forceFeedback){
13229                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13230                 }
13231                 
13232             }
13233             
13234         }
13235         
13236         this.fireEvent('invalid', this, msg);
13237     },
13238     // private
13239     SafariOnKeyDown : function(event)
13240     {
13241         // this is a workaround for a password hang bug on chrome/ webkit.
13242         if (this.inputEl().dom.type != 'password') {
13243             return;
13244         }
13245         
13246         var isSelectAll = false;
13247         
13248         if(this.inputEl().dom.selectionEnd > 0){
13249             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13250         }
13251         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13252             event.preventDefault();
13253             this.setValue('');
13254             return;
13255         }
13256         
13257         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13258             
13259             event.preventDefault();
13260             // this is very hacky as keydown always get's upper case.
13261             //
13262             var cc = String.fromCharCode(event.getCharCode());
13263             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13264             
13265         }
13266     },
13267     adjustWidth : function(tag, w){
13268         tag = tag.toLowerCase();
13269         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13270             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13271                 if(tag == 'input'){
13272                     return w + 2;
13273                 }
13274                 if(tag == 'textarea'){
13275                     return w-2;
13276                 }
13277             }else if(Roo.isOpera){
13278                 if(tag == 'input'){
13279                     return w + 2;
13280                 }
13281                 if(tag == 'textarea'){
13282                     return w-2;
13283                 }
13284             }
13285         }
13286         return w;
13287     },
13288     
13289     setFieldLabel : function(v)
13290     {
13291         if(!this.rendered){
13292             return;
13293         }
13294         
13295         if(this.indicatorEl()){
13296             var ar = this.el.select('label > span',true);
13297             
13298             if (ar.elements.length) {
13299                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13300                 this.fieldLabel = v;
13301                 return;
13302             }
13303             
13304             var br = this.el.select('label',true);
13305             
13306             if(br.elements.length) {
13307                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13308                 this.fieldLabel = v;
13309                 return;
13310             }
13311             
13312             Roo.log('Cannot Found any of label > span || label in input');
13313             return;
13314         }
13315         
13316         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13317         this.fieldLabel = v;
13318         
13319         
13320     }
13321 });
13322
13323  
13324 /*
13325  * - LGPL
13326  *
13327  * Input
13328  * 
13329  */
13330
13331 /**
13332  * @class Roo.bootstrap.form.TextArea
13333  * @extends Roo.bootstrap.form.Input
13334  * Bootstrap TextArea class
13335  * @cfg {Number} cols Specifies the visible width of a text area
13336  * @cfg {Number} rows Specifies the visible number of lines in a text area
13337  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13338  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13339  * @cfg {string} html text
13340  * 
13341  * @constructor
13342  * Create a new TextArea
13343  * @param {Object} config The config object
13344  */
13345
13346 Roo.bootstrap.form.TextArea = function(config){
13347     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13348    
13349 };
13350
13351 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13352      
13353     cols : false,
13354     rows : 5,
13355     readOnly : false,
13356     warp : 'soft',
13357     resize : false,
13358     value: false,
13359     html: false,
13360     
13361     getAutoCreate : function(){
13362         
13363         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13364         
13365         var id = Roo.id();
13366         
13367         var cfg = {};
13368         
13369         if(this.inputType != 'hidden'){
13370             cfg.cls = 'form-group' //input-group
13371         }
13372         
13373         var input =  {
13374             tag: 'textarea',
13375             id : id,
13376             warp : this.warp,
13377             rows : this.rows,
13378             value : this.value || '',
13379             html: this.html || '',
13380             cls : 'form-control',
13381             placeholder : this.placeholder || '' 
13382             
13383         };
13384         
13385         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13386             input.maxLength = this.maxLength;
13387         }
13388         
13389         if(this.resize){
13390             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13391         }
13392         
13393         if(this.cols){
13394             input.cols = this.cols;
13395         }
13396         
13397         if (this.readOnly) {
13398             input.readonly = true;
13399         }
13400         
13401         if (this.name) {
13402             input.name = this.name;
13403         }
13404         
13405         if (this.size) {
13406             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13407         }
13408         
13409         var settings=this;
13410         ['xs','sm','md','lg'].map(function(size){
13411             if (settings[size]) {
13412                 cfg.cls += ' col-' + size + '-' + settings[size];
13413             }
13414         });
13415         
13416         var inputblock = input;
13417         
13418         if(this.hasFeedback && !this.allowBlank){
13419             
13420             var feedback = {
13421                 tag: 'span',
13422                 cls: 'glyphicon form-control-feedback'
13423             };
13424
13425             inputblock = {
13426                 cls : 'has-feedback',
13427                 cn :  [
13428                     input,
13429                     feedback
13430                 ] 
13431             };  
13432         }
13433         
13434         
13435         if (this.before || this.after) {
13436             
13437             inputblock = {
13438                 cls : 'input-group',
13439                 cn :  [] 
13440             };
13441             if (this.before) {
13442                 inputblock.cn.push({
13443                     tag :'span',
13444                     cls : 'input-group-addon',
13445                     html : this.before
13446                 });
13447             }
13448             
13449             inputblock.cn.push(input);
13450             
13451             if(this.hasFeedback && !this.allowBlank){
13452                 inputblock.cls += ' has-feedback';
13453                 inputblock.cn.push(feedback);
13454             }
13455             
13456             if (this.after) {
13457                 inputblock.cn.push({
13458                     tag :'span',
13459                     cls : 'input-group-addon',
13460                     html : this.after
13461                 });
13462             }
13463             
13464         }
13465         
13466         if (align ==='left' && this.fieldLabel.length) {
13467             cfg.cn = [
13468                 {
13469                     tag: 'label',
13470                     'for' :  id,
13471                     cls : 'control-label',
13472                     html : this.fieldLabel
13473                 },
13474                 {
13475                     cls : "",
13476                     cn: [
13477                         inputblock
13478                     ]
13479                 }
13480
13481             ];
13482             
13483             if(this.labelWidth > 12){
13484                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13485             }
13486
13487             if(this.labelWidth < 13 && this.labelmd == 0){
13488                 this.labelmd = this.labelWidth;
13489             }
13490
13491             if(this.labellg > 0){
13492                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13493                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13494             }
13495
13496             if(this.labelmd > 0){
13497                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13498                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13499             }
13500
13501             if(this.labelsm > 0){
13502                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13503                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13504             }
13505
13506             if(this.labelxs > 0){
13507                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13508                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13509             }
13510             
13511         } else if ( this.fieldLabel.length) {
13512             cfg.cn = [
13513
13514                {
13515                    tag: 'label',
13516                    //cls : 'input-group-addon',
13517                    html : this.fieldLabel
13518
13519                },
13520
13521                inputblock
13522
13523            ];
13524
13525         } else {
13526
13527             cfg.cn = [
13528
13529                 inputblock
13530
13531             ];
13532                 
13533         }
13534         
13535         if (this.disabled) {
13536             input.disabled=true;
13537         }
13538         
13539         return cfg;
13540         
13541     },
13542     /**
13543      * return the real textarea element.
13544      */
13545     inputEl: function ()
13546     {
13547         return this.el.select('textarea.form-control',true).first();
13548     },
13549     
13550     /**
13551      * Clear any invalid styles/messages for this field
13552      */
13553     clearInvalid : function()
13554     {
13555         
13556         if(!this.el || this.preventMark){ // not rendered
13557             return;
13558         }
13559         
13560         var label = this.el.select('label', true).first();
13561         var icon = this.el.select('i.fa-star', true).first();
13562         
13563         if(label && icon){
13564             icon.remove();
13565         }
13566         this.el.removeClass( this.validClass);
13567         this.inputEl().removeClass('is-invalid');
13568          
13569         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13570             
13571             var feedback = this.el.select('.form-control-feedback', true).first();
13572             
13573             if(feedback){
13574                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13575             }
13576             
13577         }
13578         
13579         this.fireEvent('valid', this);
13580     },
13581     
13582      /**
13583      * Mark this field as valid
13584      */
13585     markValid : function()
13586     {
13587         if(!this.el  || this.preventMark){ // not rendered
13588             return;
13589         }
13590         
13591         this.el.removeClass([this.invalidClass, this.validClass]);
13592         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13593         
13594         var feedback = this.el.select('.form-control-feedback', true).first();
13595             
13596         if(feedback){
13597             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13598         }
13599
13600         if(this.disabled || this.allowBlank){
13601             return;
13602         }
13603         
13604         var label = this.el.select('label', true).first();
13605         var icon = this.el.select('i.fa-star', true).first();
13606         
13607         if(label && icon){
13608             icon.remove();
13609         }
13610         if (Roo.bootstrap.version == 3) {
13611             this.el.addClass(this.validClass);
13612         } else {
13613             this.inputEl().addClass('is-valid');
13614         }
13615         
13616         
13617         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13618             
13619             var feedback = this.el.select('.form-control-feedback', true).first();
13620             
13621             if(feedback){
13622                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13623                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13624             }
13625             
13626         }
13627         
13628         this.fireEvent('valid', this);
13629     },
13630     
13631      /**
13632      * Mark this field as invalid
13633      * @param {String} msg The validation message
13634      */
13635     markInvalid : function(msg)
13636     {
13637         if(!this.el  || this.preventMark){ // not rendered
13638             return;
13639         }
13640         
13641         this.el.removeClass([this.invalidClass, this.validClass]);
13642         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13643         
13644         var feedback = this.el.select('.form-control-feedback', true).first();
13645             
13646         if(feedback){
13647             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13648         }
13649
13650         if(this.disabled || this.allowBlank){
13651             return;
13652         }
13653         
13654         var label = this.el.select('label', true).first();
13655         var icon = this.el.select('i.fa-star', true).first();
13656         
13657         if(!this.getValue().length && label && !icon){
13658             this.el.createChild({
13659                 tag : 'i',
13660                 cls : 'text-danger fa fa-lg fa-star',
13661                 tooltip : 'This field is required',
13662                 style : 'margin-right:5px;'
13663             }, label, true);
13664         }
13665         
13666         if (Roo.bootstrap.version == 3) {
13667             this.el.addClass(this.invalidClass);
13668         } else {
13669             this.inputEl().addClass('is-invalid');
13670         }
13671         
13672         // fixme ... this may be depricated need to test..
13673         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13674             
13675             var feedback = this.el.select('.form-control-feedback', true).first();
13676             
13677             if(feedback){
13678                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13679                 
13680                 if(this.getValue().length || this.forceFeedback){
13681                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13682                 }
13683                 
13684             }
13685             
13686         }
13687         
13688         this.fireEvent('invalid', this, msg);
13689     }
13690 });
13691
13692  
13693 /*
13694  * - LGPL
13695  *
13696  * trigger field - base class for combo..
13697  * 
13698  */
13699  
13700 /**
13701  * @class Roo.bootstrap.form.TriggerField
13702  * @extends Roo.bootstrap.form.Input
13703  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13704  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13705  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13706  * for which you can provide a custom implementation.  For example:
13707  * <pre><code>
13708 var trigger = new Roo.bootstrap.form.TriggerField();
13709 trigger.onTriggerClick = myTriggerFn;
13710 trigger.applyTo('my-field');
13711 </code></pre>
13712  *
13713  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13714  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13715  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13716  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13717  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13718
13719  * @constructor
13720  * Create a new TriggerField.
13721  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13722  * to the base TextField)
13723  */
13724 Roo.bootstrap.form.TriggerField = function(config){
13725     this.mimicing = false;
13726     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13727 };
13728
13729 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13730     /**
13731      * @cfg {String} triggerClass A CSS class to apply to the trigger
13732      */
13733      /**
13734      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13735      */
13736     hideTrigger:false,
13737
13738     /**
13739      * @cfg {Boolean} removable (true|false) special filter default false
13740      */
13741     removable : false,
13742     
13743     /** @cfg {Boolean} grow @hide */
13744     /** @cfg {Number} growMin @hide */
13745     /** @cfg {Number} growMax @hide */
13746
13747     /**
13748      * @hide 
13749      * @method
13750      */
13751     autoSize: Roo.emptyFn,
13752     // private
13753     monitorTab : true,
13754     // private
13755     deferHeight : true,
13756
13757     
13758     actionMode : 'wrap',
13759     
13760     caret : false,
13761     
13762     
13763     getAutoCreate : function(){
13764        
13765         var align = this.labelAlign || this.parentLabelAlign();
13766         
13767         var id = Roo.id();
13768         
13769         var cfg = {
13770             cls: 'form-group' //input-group
13771         };
13772         
13773         
13774         var input =  {
13775             tag: 'input',
13776             id : id,
13777             type : this.inputType,
13778             cls : 'form-control',
13779             autocomplete: 'new-password',
13780             placeholder : this.placeholder || '' 
13781             
13782         };
13783         if (this.name) {
13784             input.name = this.name;
13785         }
13786         if (this.size) {
13787             input.cls += ' input-' + this.size;
13788         }
13789         
13790         if (this.disabled) {
13791             input.disabled=true;
13792         }
13793         
13794         var inputblock = input;
13795         
13796         if(this.hasFeedback && !this.allowBlank){
13797             
13798             var feedback = {
13799                 tag: 'span',
13800                 cls: 'glyphicon form-control-feedback'
13801             };
13802             
13803             if(this.removable && !this.editable  ){
13804                 inputblock = {
13805                     cls : 'has-feedback',
13806                     cn :  [
13807                         inputblock,
13808                         {
13809                             tag: 'button',
13810                             html : 'x',
13811                             cls : 'roo-combo-removable-btn close'
13812                         },
13813                         feedback
13814                     ] 
13815                 };
13816             } else {
13817                 inputblock = {
13818                     cls : 'has-feedback',
13819                     cn :  [
13820                         inputblock,
13821                         feedback
13822                     ] 
13823                 };
13824             }
13825
13826         } else {
13827             if(this.removable && !this.editable ){
13828                 inputblock = {
13829                     cls : 'roo-removable',
13830                     cn :  [
13831                         inputblock,
13832                         {
13833                             tag: 'button',
13834                             html : 'x',
13835                             cls : 'roo-combo-removable-btn close'
13836                         }
13837                     ] 
13838                 };
13839             }
13840         }
13841         
13842         if (this.before || this.after) {
13843             
13844             inputblock = {
13845                 cls : 'input-group',
13846                 cn :  [] 
13847             };
13848             if (this.before) {
13849                 inputblock.cn.push({
13850                     tag :'span',
13851                     cls : 'input-group-addon input-group-prepend input-group-text',
13852                     html : this.before
13853                 });
13854             }
13855             
13856             inputblock.cn.push(input);
13857             
13858             if(this.hasFeedback && !this.allowBlank){
13859                 inputblock.cls += ' has-feedback';
13860                 inputblock.cn.push(feedback);
13861             }
13862             
13863             if (this.after) {
13864                 inputblock.cn.push({
13865                     tag :'span',
13866                     cls : 'input-group-addon input-group-append input-group-text',
13867                     html : this.after
13868                 });
13869             }
13870             
13871         };
13872         
13873       
13874         
13875         var ibwrap = inputblock;
13876         
13877         if(this.multiple){
13878             ibwrap = {
13879                 tag: 'ul',
13880                 cls: 'roo-select2-choices',
13881                 cn:[
13882                     {
13883                         tag: 'li',
13884                         cls: 'roo-select2-search-field',
13885                         cn: [
13886
13887                             inputblock
13888                         ]
13889                     }
13890                 ]
13891             };
13892                 
13893         }
13894         
13895         var combobox = {
13896             cls: 'roo-select2-container input-group',
13897             cn: [
13898                  {
13899                     tag: 'input',
13900                     type : 'hidden',
13901                     cls: 'form-hidden-field'
13902                 },
13903                 ibwrap
13904             ]
13905         };
13906         
13907         if(!this.multiple && this.showToggleBtn){
13908             
13909             var caret = {
13910                         tag: 'span',
13911                         cls: 'caret'
13912              };
13913             if (this.caret != false) {
13914                 caret = {
13915                      tag: 'i',
13916                      cls: 'fa fa-' + this.caret
13917                 };
13918                 
13919             }
13920             
13921             combobox.cn.push({
13922                 tag :'span',
13923                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13924                 cn : [
13925                     Roo.bootstrap.version == 3 ? caret : '',
13926                     {
13927                         tag: 'span',
13928                         cls: 'combobox-clear',
13929                         cn  : [
13930                             {
13931                                 tag : 'i',
13932                                 cls: 'icon-remove'
13933                             }
13934                         ]
13935                     }
13936                 ]
13937
13938             })
13939         }
13940         
13941         if(this.multiple){
13942             combobox.cls += ' roo-select2-container-multi';
13943         }
13944          var indicator = {
13945             tag : 'i',
13946             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13947             tooltip : 'This field is required'
13948         };
13949         if (Roo.bootstrap.version == 4) {
13950             indicator = {
13951                 tag : 'i',
13952                 style : 'display:none'
13953             };
13954         }
13955         
13956         
13957         if (align ==='left' && this.fieldLabel.length) {
13958             
13959             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13960
13961             cfg.cn = [
13962                 indicator,
13963                 {
13964                     tag: 'label',
13965                     'for' :  id,
13966                     cls : 'control-label',
13967                     html : this.fieldLabel
13968
13969                 },
13970                 {
13971                     cls : "", 
13972                     cn: [
13973                         combobox
13974                     ]
13975                 }
13976
13977             ];
13978             
13979             var labelCfg = cfg.cn[1];
13980             var contentCfg = cfg.cn[2];
13981             
13982             if(this.indicatorpos == 'right'){
13983                 cfg.cn = [
13984                     {
13985                         tag: 'label',
13986                         'for' :  id,
13987                         cls : 'control-label',
13988                         cn : [
13989                             {
13990                                 tag : 'span',
13991                                 html : this.fieldLabel
13992                             },
13993                             indicator
13994                         ]
13995                     },
13996                     {
13997                         cls : "", 
13998                         cn: [
13999                             combobox
14000                         ]
14001                     }
14002
14003                 ];
14004                 
14005                 labelCfg = cfg.cn[0];
14006                 contentCfg = cfg.cn[1];
14007             }
14008             
14009             if(this.labelWidth > 12){
14010                 labelCfg.style = "width: " + this.labelWidth + 'px';
14011             }
14012             
14013             if(this.labelWidth < 13 && this.labelmd == 0){
14014                 this.labelmd = this.labelWidth;
14015             }
14016             
14017             if(this.labellg > 0){
14018                 labelCfg.cls += ' col-lg-' + this.labellg;
14019                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14020             }
14021             
14022             if(this.labelmd > 0){
14023                 labelCfg.cls += ' col-md-' + this.labelmd;
14024                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14025             }
14026             
14027             if(this.labelsm > 0){
14028                 labelCfg.cls += ' col-sm-' + this.labelsm;
14029                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14030             }
14031             
14032             if(this.labelxs > 0){
14033                 labelCfg.cls += ' col-xs-' + this.labelxs;
14034                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14035             }
14036             
14037         } else if ( this.fieldLabel.length) {
14038 //                Roo.log(" label");
14039             cfg.cn = [
14040                 indicator,
14041                {
14042                    tag: 'label',
14043                    //cls : 'input-group-addon',
14044                    html : this.fieldLabel
14045
14046                },
14047
14048                combobox
14049
14050             ];
14051             
14052             if(this.indicatorpos == 'right'){
14053                 
14054                 cfg.cn = [
14055                     {
14056                        tag: 'label',
14057                        cn : [
14058                            {
14059                                tag : 'span',
14060                                html : this.fieldLabel
14061                            },
14062                            indicator
14063                        ]
14064
14065                     },
14066                     combobox
14067
14068                 ];
14069
14070             }
14071
14072         } else {
14073             
14074 //                Roo.log(" no label && no align");
14075                 cfg = combobox
14076                      
14077                 
14078         }
14079         
14080         var settings=this;
14081         ['xs','sm','md','lg'].map(function(size){
14082             if (settings[size]) {
14083                 cfg.cls += ' col-' + size + '-' + settings[size];
14084             }
14085         });
14086         
14087         return cfg;
14088         
14089     },
14090     
14091     
14092     
14093     // private
14094     onResize : function(w, h){
14095 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14096 //        if(typeof w == 'number'){
14097 //            var x = w - this.trigger.getWidth();
14098 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14099 //            this.trigger.setStyle('left', x+'px');
14100 //        }
14101     },
14102
14103     // private
14104     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14105
14106     // private
14107     getResizeEl : function(){
14108         return this.inputEl();
14109     },
14110
14111     // private
14112     getPositionEl : function(){
14113         return this.inputEl();
14114     },
14115
14116     // private
14117     alignErrorIcon : function(){
14118         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14119     },
14120
14121     // private
14122     initEvents : function(){
14123         
14124         this.createList();
14125         
14126         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14127         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14128         if(!this.multiple && this.showToggleBtn){
14129             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14130             if(this.hideTrigger){
14131                 this.trigger.setDisplayed(false);
14132             }
14133             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14134         }
14135         
14136         if(this.multiple){
14137             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14138         }
14139         
14140         if(this.removable && !this.editable && !this.tickable){
14141             var close = this.closeTriggerEl();
14142             
14143             if(close){
14144                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14145                 close.on('click', this.removeBtnClick, this, close);
14146             }
14147         }
14148         
14149         //this.trigger.addClassOnOver('x-form-trigger-over');
14150         //this.trigger.addClassOnClick('x-form-trigger-click');
14151         
14152         //if(!this.width){
14153         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14154         //}
14155     },
14156     
14157     closeTriggerEl : function()
14158     {
14159         var close = this.el.select('.roo-combo-removable-btn', true).first();
14160         return close ? close : false;
14161     },
14162     
14163     removeBtnClick : function(e, h, el)
14164     {
14165         e.preventDefault();
14166         
14167         if(this.fireEvent("remove", this) !== false){
14168             this.reset();
14169             this.fireEvent("afterremove", this)
14170         }
14171     },
14172     
14173     createList : function()
14174     {
14175         this.list = Roo.get(document.body).createChild({
14176             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14177             cls: 'typeahead typeahead-long dropdown-menu shadow',
14178             style: 'display:none'
14179         });
14180         
14181         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14182         
14183     },
14184
14185     // private
14186     initTrigger : function(){
14187        
14188     },
14189
14190     // private
14191     onDestroy : function(){
14192         if(this.trigger){
14193             this.trigger.removeAllListeners();
14194           //  this.trigger.remove();
14195         }
14196         //if(this.wrap){
14197         //    this.wrap.remove();
14198         //}
14199         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14200     },
14201
14202     // private
14203     onFocus : function(){
14204         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14205         /*
14206         if(!this.mimicing){
14207             this.wrap.addClass('x-trigger-wrap-focus');
14208             this.mimicing = true;
14209             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14210             if(this.monitorTab){
14211                 this.el.on("keydown", this.checkTab, this);
14212             }
14213         }
14214         */
14215     },
14216
14217     // private
14218     checkTab : function(e){
14219         if(e.getKey() == e.TAB){
14220             this.triggerBlur();
14221         }
14222     },
14223
14224     // private
14225     onBlur : function(){
14226         // do nothing
14227     },
14228
14229     // private
14230     mimicBlur : function(e, t){
14231         /*
14232         if(!this.wrap.contains(t) && this.validateBlur()){
14233             this.triggerBlur();
14234         }
14235         */
14236     },
14237
14238     // private
14239     triggerBlur : function(){
14240         this.mimicing = false;
14241         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14242         if(this.monitorTab){
14243             this.el.un("keydown", this.checkTab, this);
14244         }
14245         //this.wrap.removeClass('x-trigger-wrap-focus');
14246         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14247     },
14248
14249     // private
14250     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14251     validateBlur : function(e, t){
14252         return true;
14253     },
14254
14255     // private
14256     onDisable : function(){
14257         this.inputEl().dom.disabled = true;
14258         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14259         //if(this.wrap){
14260         //    this.wrap.addClass('x-item-disabled');
14261         //}
14262     },
14263
14264     // private
14265     onEnable : function(){
14266         this.inputEl().dom.disabled = false;
14267         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14268         //if(this.wrap){
14269         //    this.el.removeClass('x-item-disabled');
14270         //}
14271     },
14272
14273     // private
14274     onShow : function(){
14275         var ae = this.getActionEl();
14276         
14277         if(ae){
14278             ae.dom.style.display = '';
14279             ae.dom.style.visibility = 'visible';
14280         }
14281     },
14282
14283     // private
14284     
14285     onHide : function(){
14286         var ae = this.getActionEl();
14287         ae.dom.style.display = 'none';
14288     },
14289
14290     /**
14291      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14292      * by an implementing function.
14293      * @method
14294      * @param {EventObject} e
14295      */
14296     onTriggerClick : Roo.emptyFn
14297 });
14298  
14299 /*
14300 * Licence: LGPL
14301 */
14302
14303 /**
14304  * @class Roo.bootstrap.form.CardUploader
14305  * @extends Roo.bootstrap.Button
14306  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14307  * @cfg {Number} errorTimeout default 3000
14308  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14309  * @cfg {Array}  html The button text.
14310
14311  *
14312  * @constructor
14313  * Create a new CardUploader
14314  * @param {Object} config The config object
14315  */
14316
14317 Roo.bootstrap.form.CardUploader = function(config){
14318     
14319  
14320     
14321     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14322     
14323     
14324     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14325         return r.data.id
14326      });
14327     
14328      this.addEvents({
14329          // raw events
14330         /**
14331          * @event preview
14332          * When a image is clicked on - and needs to display a slideshow or similar..
14333          * @param {Roo.bootstrap.Card} this
14334          * @param {Object} The image information data 
14335          *
14336          */
14337         'preview' : true,
14338          /**
14339          * @event download
14340          * When a the download link is clicked
14341          * @param {Roo.bootstrap.Card} this
14342          * @param {Object} The image information data  contains 
14343          */
14344         'download' : true
14345         
14346     });
14347 };
14348  
14349 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14350     
14351      
14352     errorTimeout : 3000,
14353      
14354     images : false,
14355    
14356     fileCollection : false,
14357     allowBlank : true,
14358     
14359     getAutoCreate : function()
14360     {
14361         
14362         var cfg =  {
14363             cls :'form-group' ,
14364             cn : [
14365                
14366                 {
14367                     tag: 'label',
14368                    //cls : 'input-group-addon',
14369                     html : this.fieldLabel
14370
14371                 },
14372
14373                 {
14374                     tag: 'input',
14375                     type : 'hidden',
14376                     name : this.name,
14377                     value : this.value,
14378                     cls : 'd-none  form-control'
14379                 },
14380                 
14381                 {
14382                     tag: 'input',
14383                     multiple : 'multiple',
14384                     type : 'file',
14385                     cls : 'd-none  roo-card-upload-selector'
14386                 },
14387                 
14388                 {
14389                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14390                 },
14391                 {
14392                     cls : 'card-columns roo-card-uploader-container'
14393                 }
14394
14395             ]
14396         };
14397            
14398          
14399         return cfg;
14400     },
14401     
14402     getChildContainer : function() /// what children are added to.
14403     {
14404         return this.containerEl;
14405     },
14406    
14407     getButtonContainer : function() /// what children are added to.
14408     {
14409         return this.el.select(".roo-card-uploader-button-container").first();
14410     },
14411    
14412     initEvents : function()
14413     {
14414         
14415         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14416         
14417         var t = this;
14418         this.addxtype({
14419             xns: Roo.bootstrap,
14420
14421             xtype : 'Button',
14422             container_method : 'getButtonContainer' ,            
14423             html :  this.html, // fix changable?
14424             cls : 'w-100 ',
14425             listeners : {
14426                 'click' : function(btn, e) {
14427                     t.onClick(e);
14428                 }
14429             }
14430         });
14431         
14432         
14433         
14434         
14435         this.urlAPI = (window.createObjectURL && window) || 
14436                                 (window.URL && URL.revokeObjectURL && URL) || 
14437                                 (window.webkitURL && webkitURL);
14438                         
14439          
14440          
14441          
14442         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14443         
14444         this.selectorEl.on('change', this.onFileSelected, this);
14445         if (this.images) {
14446             var t = this;
14447             this.images.forEach(function(img) {
14448                 t.addCard(img)
14449             });
14450             this.images = false;
14451         }
14452         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14453          
14454        
14455     },
14456     
14457    
14458     onClick : function(e)
14459     {
14460         e.preventDefault();
14461          
14462         this.selectorEl.dom.click();
14463          
14464     },
14465     
14466     onFileSelected : function(e)
14467     {
14468         e.preventDefault();
14469         
14470         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14471             return;
14472         }
14473         
14474         Roo.each(this.selectorEl.dom.files, function(file){    
14475             this.addFile(file);
14476         }, this);
14477          
14478     },
14479     
14480       
14481     
14482       
14483     
14484     addFile : function(file)
14485     {
14486            
14487         if(typeof(file) === 'string'){
14488             throw "Add file by name?"; // should not happen
14489             return;
14490         }
14491         
14492         if(!file || !this.urlAPI){
14493             return;
14494         }
14495         
14496         // file;
14497         // file.type;
14498         
14499         var _this = this;
14500         
14501         
14502         var url = _this.urlAPI.createObjectURL( file);
14503            
14504         this.addCard({
14505             id : Roo.bootstrap.form.CardUploader.ID--,
14506             is_uploaded : false,
14507             src : url,
14508             srcfile : file,
14509             title : file.name,
14510             mimetype : file.type,
14511             preview : false,
14512             is_deleted : 0
14513         });
14514         
14515     },
14516     
14517     /**
14518      * addCard - add an Attachment to the uploader
14519      * @param data - the data about the image to upload
14520      *
14521      * {
14522           id : 123
14523           title : "Title of file",
14524           is_uploaded : false,
14525           src : "http://.....",
14526           srcfile : { the File upload object },
14527           mimetype : file.type,
14528           preview : false,
14529           is_deleted : 0
14530           .. any other data...
14531         }
14532      *
14533      * 
14534     */
14535     
14536     addCard : function (data)
14537     {
14538         // hidden input element?
14539         // if the file is not an image...
14540         //then we need to use something other that and header_image
14541         var t = this;
14542         //   remove.....
14543         var footer = [
14544             {
14545                 xns : Roo.bootstrap,
14546                 xtype : 'CardFooter',
14547                  items: [
14548                     {
14549                         xns : Roo.bootstrap,
14550                         xtype : 'Element',
14551                         cls : 'd-flex',
14552                         items : [
14553                             
14554                             {
14555                                 xns : Roo.bootstrap,
14556                                 xtype : 'Button',
14557                                 html : String.format("<small>{0}</small>", data.title),
14558                                 cls : 'col-10 text-left',
14559                                 size: 'sm',
14560                                 weight: 'link',
14561                                 fa : 'download',
14562                                 listeners : {
14563                                     click : function() {
14564                                      
14565                                         t.fireEvent( "download", t, data );
14566                                     }
14567                                 }
14568                             },
14569                           
14570                             {
14571                                 xns : Roo.bootstrap,
14572                                 xtype : 'Button',
14573                                 style: 'max-height: 28px; ',
14574                                 size : 'sm',
14575                                 weight: 'danger',
14576                                 cls : 'col-2',
14577                                 fa : 'times',
14578                                 listeners : {
14579                                     click : function() {
14580                                         t.removeCard(data.id)
14581                                     }
14582                                 }
14583                             }
14584                         ]
14585                     }
14586                     
14587                 ] 
14588             }
14589             
14590         ];
14591         
14592         var cn = this.addxtype(
14593             {
14594                  
14595                 xns : Roo.bootstrap,
14596                 xtype : 'Card',
14597                 closeable : true,
14598                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14599                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14600                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14601                 data : data,
14602                 html : false,
14603                  
14604                 items : footer,
14605                 initEvents : function() {
14606                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14607                     var card = this;
14608                     this.imgEl = this.el.select('.card-img-top').first();
14609                     if (this.imgEl) {
14610                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14611                         this.imgEl.set({ 'pointer' : 'cursor' });
14612                                   
14613                     }
14614                     this.getCardFooter().addClass('p-1');
14615                     
14616                   
14617                 }
14618                 
14619             }
14620         );
14621         // dont' really need ot update items.
14622         // this.items.push(cn);
14623         this.fileCollection.add(cn);
14624         
14625         if (!data.srcfile) {
14626             this.updateInput();
14627             return;
14628         }
14629             
14630         var _t = this;
14631         var reader = new FileReader();
14632         reader.addEventListener("load", function() {  
14633             data.srcdata =  reader.result;
14634             _t.updateInput();
14635         });
14636         reader.readAsDataURL(data.srcfile);
14637         
14638         
14639         
14640     },
14641     removeCard : function(id)
14642     {
14643         
14644         var card  = this.fileCollection.get(id);
14645         card.data.is_deleted = 1;
14646         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14647         //this.fileCollection.remove(card);
14648         //this.items = this.items.filter(function(e) { return e != card });
14649         // dont' really need ot update items.
14650         card.el.dom.parentNode.removeChild(card.el.dom);
14651         this.updateInput();
14652
14653         
14654     },
14655     reset: function()
14656     {
14657         this.fileCollection.each(function(card) {
14658             if (card.el.dom && card.el.dom.parentNode) {
14659                 card.el.dom.parentNode.removeChild(card.el.dom);
14660             }
14661         });
14662         this.fileCollection.clear();
14663         this.updateInput();
14664     },
14665     
14666     updateInput : function()
14667     {
14668          var data = [];
14669         this.fileCollection.each(function(e) {
14670             data.push(e.data);
14671             
14672         });
14673         this.inputEl().dom.value = JSON.stringify(data);
14674         
14675         
14676         
14677     }
14678     
14679     
14680 });
14681
14682
14683 Roo.bootstrap.form.CardUploader.ID = -1;/*
14684  * Based on:
14685  * Ext JS Library 1.1.1
14686  * Copyright(c) 2006-2007, Ext JS, LLC.
14687  *
14688  * Originally Released Under LGPL - original licence link has changed is not relivant.
14689  *
14690  * Fork - LGPL
14691  * <script type="text/javascript">
14692  */
14693
14694
14695 /**
14696  * @class Roo.data.SortTypes
14697  * @static
14698  * Defines the default sorting (casting?) comparison functions used when sorting data.
14699  */
14700 Roo.data.SortTypes = {
14701     /**
14702      * Default sort that does nothing
14703      * @param {Mixed} s The value being converted
14704      * @return {Mixed} The comparison value
14705      */
14706     none : function(s){
14707         return s;
14708     },
14709     
14710     /**
14711      * The regular expression used to strip tags
14712      * @type {RegExp}
14713      * @property
14714      */
14715     stripTagsRE : /<\/?[^>]+>/gi,
14716     
14717     /**
14718      * Strips all HTML tags to sort on text only
14719      * @param {Mixed} s The value being converted
14720      * @return {String} The comparison value
14721      */
14722     asText : function(s){
14723         return String(s).replace(this.stripTagsRE, "");
14724     },
14725     
14726     /**
14727      * Strips all HTML tags to sort on text only - Case insensitive
14728      * @param {Mixed} s The value being converted
14729      * @return {String} The comparison value
14730      */
14731     asUCText : function(s){
14732         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14733     },
14734     
14735     /**
14736      * Case insensitive string
14737      * @param {Mixed} s The value being converted
14738      * @return {String} The comparison value
14739      */
14740     asUCString : function(s) {
14741         return String(s).toUpperCase();
14742     },
14743     
14744     /**
14745      * Date sorting
14746      * @param {Mixed} s The value being converted
14747      * @return {Number} The comparison value
14748      */
14749     asDate : function(s) {
14750         if(!s){
14751             return 0;
14752         }
14753         if(s instanceof Date){
14754             return s.getTime();
14755         }
14756         return Date.parse(String(s));
14757     },
14758     
14759     /**
14760      * Float sorting
14761      * @param {Mixed} s The value being converted
14762      * @return {Float} The comparison value
14763      */
14764     asFloat : function(s) {
14765         var val = parseFloat(String(s).replace(/,/g, ""));
14766         if(isNaN(val)) {
14767             val = 0;
14768         }
14769         return val;
14770     },
14771     
14772     /**
14773      * Integer sorting
14774      * @param {Mixed} s The value being converted
14775      * @return {Number} The comparison value
14776      */
14777     asInt : function(s) {
14778         var val = parseInt(String(s).replace(/,/g, ""));
14779         if(isNaN(val)) {
14780             val = 0;
14781         }
14782         return val;
14783     }
14784 };/*
14785  * Based on:
14786  * Ext JS Library 1.1.1
14787  * Copyright(c) 2006-2007, Ext JS, LLC.
14788  *
14789  * Originally Released Under LGPL - original licence link has changed is not relivant.
14790  *
14791  * Fork - LGPL
14792  * <script type="text/javascript">
14793  */
14794
14795 /**
14796 * @class Roo.data.Record
14797  * Instances of this class encapsulate both record <em>definition</em> information, and record
14798  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14799  * to access Records cached in an {@link Roo.data.Store} object.<br>
14800  * <p>
14801  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14802  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14803  * objects.<br>
14804  * <p>
14805  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14806  * @constructor
14807  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14808  * {@link #create}. The parameters are the same.
14809  * @param {Array} data An associative Array of data values keyed by the field name.
14810  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14811  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14812  * not specified an integer id is generated.
14813  */
14814 Roo.data.Record = function(data, id){
14815     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14816     this.data = data;
14817 };
14818
14819 /**
14820  * Generate a constructor for a specific record layout.
14821  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14822  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14823  * Each field definition object may contain the following properties: <ul>
14824  * <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,
14825  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14826  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14827  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14828  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14829  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14830  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14831  * this may be omitted.</p></li>
14832  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14833  * <ul><li>auto (Default, implies no conversion)</li>
14834  * <li>string</li>
14835  * <li>int</li>
14836  * <li>float</li>
14837  * <li>boolean</li>
14838  * <li>date</li></ul></p></li>
14839  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14840  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14841  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14842  * by the Reader into an object that will be stored in the Record. It is passed the
14843  * following parameters:<ul>
14844  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14845  * </ul></p></li>
14846  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14847  * </ul>
14848  * <br>usage:<br><pre><code>
14849 var TopicRecord = Roo.data.Record.create(
14850     {name: 'title', mapping: 'topic_title'},
14851     {name: 'author', mapping: 'username'},
14852     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14853     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14854     {name: 'lastPoster', mapping: 'user2'},
14855     {name: 'excerpt', mapping: 'post_text'}
14856 );
14857
14858 var myNewRecord = new TopicRecord({
14859     title: 'Do my job please',
14860     author: 'noobie',
14861     totalPosts: 1,
14862     lastPost: new Date(),
14863     lastPoster: 'Animal',
14864     excerpt: 'No way dude!'
14865 });
14866 myStore.add(myNewRecord);
14867 </code></pre>
14868  * @method create
14869  * @static
14870  */
14871 Roo.data.Record.create = function(o){
14872     var f = function(){
14873         f.superclass.constructor.apply(this, arguments);
14874     };
14875     Roo.extend(f, Roo.data.Record);
14876     var p = f.prototype;
14877     p.fields = new Roo.util.MixedCollection(false, function(field){
14878         return field.name;
14879     });
14880     for(var i = 0, len = o.length; i < len; i++){
14881         p.fields.add(new Roo.data.Field(o[i]));
14882     }
14883     f.getField = function(name){
14884         return p.fields.get(name);  
14885     };
14886     return f;
14887 };
14888
14889 Roo.data.Record.AUTO_ID = 1000;
14890 Roo.data.Record.EDIT = 'edit';
14891 Roo.data.Record.REJECT = 'reject';
14892 Roo.data.Record.COMMIT = 'commit';
14893
14894 Roo.data.Record.prototype = {
14895     /**
14896      * Readonly flag - true if this record has been modified.
14897      * @type Boolean
14898      */
14899     dirty : false,
14900     editing : false,
14901     error: null,
14902     modified: null,
14903
14904     // private
14905     join : function(store){
14906         this.store = store;
14907     },
14908
14909     /**
14910      * Set the named field to the specified value.
14911      * @param {String} name The name of the field to set.
14912      * @param {Object} value The value to set the field to.
14913      */
14914     set : function(name, value){
14915         if(this.data[name] == value){
14916             return;
14917         }
14918         this.dirty = true;
14919         if(!this.modified){
14920             this.modified = {};
14921         }
14922         if(typeof this.modified[name] == 'undefined'){
14923             this.modified[name] = this.data[name];
14924         }
14925         this.data[name] = value;
14926         if(!this.editing && this.store){
14927             this.store.afterEdit(this);
14928         }       
14929     },
14930
14931     /**
14932      * Get the value of the named field.
14933      * @param {String} name The name of the field to get the value of.
14934      * @return {Object} The value of the field.
14935      */
14936     get : function(name){
14937         return this.data[name]; 
14938     },
14939
14940     // private
14941     beginEdit : function(){
14942         this.editing = true;
14943         this.modified = {}; 
14944     },
14945
14946     // private
14947     cancelEdit : function(){
14948         this.editing = false;
14949         delete this.modified;
14950     },
14951
14952     // private
14953     endEdit : function(){
14954         this.editing = false;
14955         if(this.dirty && this.store){
14956             this.store.afterEdit(this);
14957         }
14958     },
14959
14960     /**
14961      * Usually called by the {@link Roo.data.Store} which owns the Record.
14962      * Rejects all changes made to the Record since either creation, or the last commit operation.
14963      * Modified fields are reverted to their original values.
14964      * <p>
14965      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14966      * of reject operations.
14967      */
14968     reject : function(){
14969         var m = this.modified;
14970         for(var n in m){
14971             if(typeof m[n] != "function"){
14972                 this.data[n] = m[n];
14973             }
14974         }
14975         this.dirty = false;
14976         delete this.modified;
14977         this.editing = false;
14978         if(this.store){
14979             this.store.afterReject(this);
14980         }
14981     },
14982
14983     /**
14984      * Usually called by the {@link Roo.data.Store} which owns the Record.
14985      * Commits all changes made to the Record since either creation, or the last commit operation.
14986      * <p>
14987      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14988      * of commit operations.
14989      */
14990     commit : function(){
14991         this.dirty = false;
14992         delete this.modified;
14993         this.editing = false;
14994         if(this.store){
14995             this.store.afterCommit(this);
14996         }
14997     },
14998
14999     // private
15000     hasError : function(){
15001         return this.error != null;
15002     },
15003
15004     // private
15005     clearError : function(){
15006         this.error = null;
15007     },
15008
15009     /**
15010      * Creates a copy of this record.
15011      * @param {String} id (optional) A new record id if you don't want to use this record's id
15012      * @return {Record}
15013      */
15014     copy : function(newId) {
15015         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15016     }
15017 };/*
15018  * Based on:
15019  * Ext JS Library 1.1.1
15020  * Copyright(c) 2006-2007, Ext JS, LLC.
15021  *
15022  * Originally Released Under LGPL - original licence link has changed is not relivant.
15023  *
15024  * Fork - LGPL
15025  * <script type="text/javascript">
15026  */
15027
15028
15029
15030 /**
15031  * @class Roo.data.Store
15032  * @extends Roo.util.Observable
15033  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15034  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15035  * <p>
15036  * 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
15037  * has no knowledge of the format of the data returned by the Proxy.<br>
15038  * <p>
15039  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15040  * instances from the data object. These records are cached and made available through accessor functions.
15041  * @constructor
15042  * Creates a new Store.
15043  * @param {Object} config A config object containing the objects needed for the Store to access data,
15044  * and read the data into Records.
15045  */
15046 Roo.data.Store = function(config){
15047     this.data = new Roo.util.MixedCollection(false);
15048     this.data.getKey = function(o){
15049         return o.id;
15050     };
15051     this.baseParams = {};
15052     // private
15053     this.paramNames = {
15054         "start" : "start",
15055         "limit" : "limit",
15056         "sort" : "sort",
15057         "dir" : "dir",
15058         "multisort" : "_multisort"
15059     };
15060
15061     if(config && config.data){
15062         this.inlineData = config.data;
15063         delete config.data;
15064     }
15065
15066     Roo.apply(this, config);
15067     
15068     if(this.reader){ // reader passed
15069         this.reader = Roo.factory(this.reader, Roo.data);
15070         this.reader.xmodule = this.xmodule || false;
15071         if(!this.recordType){
15072             this.recordType = this.reader.recordType;
15073         }
15074         if(this.reader.onMetaChange){
15075             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15076         }
15077     }
15078
15079     if(this.recordType){
15080         this.fields = this.recordType.prototype.fields;
15081     }
15082     this.modified = [];
15083
15084     this.addEvents({
15085         /**
15086          * @event datachanged
15087          * Fires when the data cache has changed, and a widget which is using this Store
15088          * as a Record cache should refresh its view.
15089          * @param {Store} this
15090          */
15091         datachanged : true,
15092         /**
15093          * @event metachange
15094          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15095          * @param {Store} this
15096          * @param {Object} meta The JSON metadata
15097          */
15098         metachange : true,
15099         /**
15100          * @event add
15101          * Fires when Records have been added to the Store
15102          * @param {Store} this
15103          * @param {Roo.data.Record[]} records The array of Records added
15104          * @param {Number} index The index at which the record(s) were added
15105          */
15106         add : true,
15107         /**
15108          * @event remove
15109          * Fires when a Record has been removed from the Store
15110          * @param {Store} this
15111          * @param {Roo.data.Record} record The Record that was removed
15112          * @param {Number} index The index at which the record was removed
15113          */
15114         remove : true,
15115         /**
15116          * @event update
15117          * Fires when a Record has been updated
15118          * @param {Store} this
15119          * @param {Roo.data.Record} record The Record that was updated
15120          * @param {String} operation The update operation being performed.  Value may be one of:
15121          * <pre><code>
15122  Roo.data.Record.EDIT
15123  Roo.data.Record.REJECT
15124  Roo.data.Record.COMMIT
15125          * </code></pre>
15126          */
15127         update : true,
15128         /**
15129          * @event clear
15130          * Fires when the data cache has been cleared.
15131          * @param {Store} this
15132          */
15133         clear : true,
15134         /**
15135          * @event beforeload
15136          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15137          * the load action will be canceled.
15138          * @param {Store} this
15139          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15140          */
15141         beforeload : true,
15142         /**
15143          * @event beforeloadadd
15144          * Fires after a new set of Records has been loaded.
15145          * @param {Store} this
15146          * @param {Roo.data.Record[]} records The Records that were loaded
15147          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15148          */
15149         beforeloadadd : true,
15150         /**
15151          * @event load
15152          * Fires after a new set of Records has been loaded, before they are added to the store.
15153          * @param {Store} this
15154          * @param {Roo.data.Record[]} records The Records that were loaded
15155          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15156          * @params {Object} return from reader
15157          */
15158         load : true,
15159         /**
15160          * @event loadexception
15161          * Fires if an exception occurs in the Proxy during loading.
15162          * Called with the signature of the Proxy's "loadexception" event.
15163          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15164          * 
15165          * @param {Proxy} 
15166          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15167          * @param {Object} load options 
15168          * @param {Object} jsonData from your request (normally this contains the Exception)
15169          */
15170         loadexception : true
15171     });
15172     
15173     if(this.proxy){
15174         this.proxy = Roo.factory(this.proxy, Roo.data);
15175         this.proxy.xmodule = this.xmodule || false;
15176         this.relayEvents(this.proxy,  ["loadexception"]);
15177     }
15178     this.sortToggle = {};
15179     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15180
15181     Roo.data.Store.superclass.constructor.call(this);
15182
15183     if(this.inlineData){
15184         this.loadData(this.inlineData);
15185         delete this.inlineData;
15186     }
15187 };
15188
15189 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15190      /**
15191     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15192     * without a remote query - used by combo/forms at present.
15193     */
15194     
15195     /**
15196     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15197     */
15198     /**
15199     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15200     */
15201     /**
15202     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15203     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15204     */
15205     /**
15206     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15207     * on any HTTP request
15208     */
15209     /**
15210     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15211     */
15212     /**
15213     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15214     */
15215     multiSort: false,
15216     /**
15217     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15218     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15219     */
15220     remoteSort : false,
15221
15222     /**
15223     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15224      * loaded or when a record is removed. (defaults to false).
15225     */
15226     pruneModifiedRecords : false,
15227
15228     // private
15229     lastOptions : null,
15230
15231     /**
15232      * Add Records to the Store and fires the add event.
15233      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15234      */
15235     add : function(records){
15236         records = [].concat(records);
15237         for(var i = 0, len = records.length; i < len; i++){
15238             records[i].join(this);
15239         }
15240         var index = this.data.length;
15241         this.data.addAll(records);
15242         this.fireEvent("add", this, records, index);
15243     },
15244
15245     /**
15246      * Remove a Record from the Store and fires the remove event.
15247      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15248      */
15249     remove : function(record){
15250         var index = this.data.indexOf(record);
15251         this.data.removeAt(index);
15252  
15253         if(this.pruneModifiedRecords){
15254             this.modified.remove(record);
15255         }
15256         this.fireEvent("remove", this, record, index);
15257     },
15258
15259     /**
15260      * Remove all Records from the Store and fires the clear event.
15261      */
15262     removeAll : function(){
15263         this.data.clear();
15264         if(this.pruneModifiedRecords){
15265             this.modified = [];
15266         }
15267         this.fireEvent("clear", this);
15268     },
15269
15270     /**
15271      * Inserts Records to the Store at the given index and fires the add event.
15272      * @param {Number} index The start index at which to insert the passed Records.
15273      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15274      */
15275     insert : function(index, records){
15276         records = [].concat(records);
15277         for(var i = 0, len = records.length; i < len; i++){
15278             this.data.insert(index, records[i]);
15279             records[i].join(this);
15280         }
15281         this.fireEvent("add", this, records, index);
15282     },
15283
15284     /**
15285      * Get the index within the cache of the passed Record.
15286      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15287      * @return {Number} The index of the passed Record. Returns -1 if not found.
15288      */
15289     indexOf : function(record){
15290         return this.data.indexOf(record);
15291     },
15292
15293     /**
15294      * Get the index within the cache of the Record with the passed id.
15295      * @param {String} id The id of the Record to find.
15296      * @return {Number} The index of the Record. Returns -1 if not found.
15297      */
15298     indexOfId : function(id){
15299         return this.data.indexOfKey(id);
15300     },
15301
15302     /**
15303      * Get the Record with the specified id.
15304      * @param {String} id The id of the Record to find.
15305      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15306      */
15307     getById : function(id){
15308         return this.data.key(id);
15309     },
15310
15311     /**
15312      * Get the Record at the specified index.
15313      * @param {Number} index The index of the Record to find.
15314      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15315      */
15316     getAt : function(index){
15317         return this.data.itemAt(index);
15318     },
15319
15320     /**
15321      * Returns a range of Records between specified indices.
15322      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15323      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15324      * @return {Roo.data.Record[]} An array of Records
15325      */
15326     getRange : function(start, end){
15327         return this.data.getRange(start, end);
15328     },
15329
15330     // private
15331     storeOptions : function(o){
15332         o = Roo.apply({}, o);
15333         delete o.callback;
15334         delete o.scope;
15335         this.lastOptions = o;
15336     },
15337
15338     /**
15339      * Loads the Record cache from the configured Proxy using the configured Reader.
15340      * <p>
15341      * If using remote paging, then the first load call must specify the <em>start</em>
15342      * and <em>limit</em> properties in the options.params property to establish the initial
15343      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15344      * <p>
15345      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15346      * and this call will return before the new data has been loaded. Perform any post-processing
15347      * in a callback function, or in a "load" event handler.</strong>
15348      * <p>
15349      * @param {Object} options An object containing properties which control loading options:<ul>
15350      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15351      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15352      * passed the following arguments:<ul>
15353      * <li>r : Roo.data.Record[]</li>
15354      * <li>options: Options object from the load call</li>
15355      * <li>success: Boolean success indicator</li></ul></li>
15356      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15357      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15358      * </ul>
15359      */
15360     load : function(options){
15361         options = options || {};
15362         if(this.fireEvent("beforeload", this, options) !== false){
15363             this.storeOptions(options);
15364             var p = Roo.apply(options.params || {}, this.baseParams);
15365             // if meta was not loaded from remote source.. try requesting it.
15366             if (!this.reader.metaFromRemote) {
15367                 p._requestMeta = 1;
15368             }
15369             if(this.sortInfo && this.remoteSort){
15370                 var pn = this.paramNames;
15371                 p[pn["sort"]] = this.sortInfo.field;
15372                 p[pn["dir"]] = this.sortInfo.direction;
15373             }
15374             if (this.multiSort) {
15375                 var pn = this.paramNames;
15376                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15377             }
15378             
15379             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15380         }
15381     },
15382
15383     /**
15384      * Reloads the Record cache from the configured Proxy using the configured Reader and
15385      * the options from the last load operation performed.
15386      * @param {Object} options (optional) An object containing properties which may override the options
15387      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15388      * the most recently used options are reused).
15389      */
15390     reload : function(options){
15391         this.load(Roo.applyIf(options||{}, this.lastOptions));
15392     },
15393
15394     // private
15395     // Called as a callback by the Reader during a load operation.
15396     loadRecords : function(o, options, success){
15397          
15398         if(!o){
15399             if(success !== false){
15400                 this.fireEvent("load", this, [], options, o);
15401             }
15402             if(options.callback){
15403                 options.callback.call(options.scope || this, [], options, false);
15404             }
15405             return;
15406         }
15407         // if data returned failure - throw an exception.
15408         if (o.success === false) {
15409             // show a message if no listener is registered.
15410             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15411                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15412             }
15413             // loadmask wil be hooked into this..
15414             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15415             return;
15416         }
15417         var r = o.records, t = o.totalRecords || r.length;
15418         
15419         this.fireEvent("beforeloadadd", this, r, options, o);
15420         
15421         if(!options || options.add !== true){
15422             if(this.pruneModifiedRecords){
15423                 this.modified = [];
15424             }
15425             for(var i = 0, len = r.length; i < len; i++){
15426                 r[i].join(this);
15427             }
15428             if(this.snapshot){
15429                 this.data = this.snapshot;
15430                 delete this.snapshot;
15431             }
15432             this.data.clear();
15433             this.data.addAll(r);
15434             this.totalLength = t;
15435             this.applySort();
15436             this.fireEvent("datachanged", this);
15437         }else{
15438             this.totalLength = Math.max(t, this.data.length+r.length);
15439             this.add(r);
15440         }
15441         
15442         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15443                 
15444             var e = new Roo.data.Record({});
15445
15446             e.set(this.parent.displayField, this.parent.emptyTitle);
15447             e.set(this.parent.valueField, '');
15448
15449             this.insert(0, e);
15450         }
15451             
15452         this.fireEvent("load", this, r, options, o);
15453         if(options.callback){
15454             options.callback.call(options.scope || this, r, options, true);
15455         }
15456     },
15457
15458
15459     /**
15460      * Loads data from a passed data block. A Reader which understands the format of the data
15461      * must have been configured in the constructor.
15462      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15463      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15464      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15465      */
15466     loadData : function(o, append){
15467         var r = this.reader.readRecords(o);
15468         this.loadRecords(r, {add: append}, true);
15469     },
15470     
15471      /**
15472      * using 'cn' the nested child reader read the child array into it's child stores.
15473      * @param {Object} rec The record with a 'children array
15474      */
15475     loadDataFromChildren : function(rec)
15476     {
15477         this.loadData(this.reader.toLoadData(rec));
15478     },
15479     
15480
15481     /**
15482      * Gets the number of cached records.
15483      * <p>
15484      * <em>If using paging, this may not be the total size of the dataset. If the data object
15485      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15486      * the data set size</em>
15487      */
15488     getCount : function(){
15489         return this.data.length || 0;
15490     },
15491
15492     /**
15493      * Gets the total number of records in the dataset as returned by the server.
15494      * <p>
15495      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15496      * the dataset size</em>
15497      */
15498     getTotalCount : function(){
15499         return this.totalLength || 0;
15500     },
15501
15502     /**
15503      * Returns the sort state of the Store as an object with two properties:
15504      * <pre><code>
15505  field {String} The name of the field by which the Records are sorted
15506  direction {String} The sort order, "ASC" or "DESC"
15507      * </code></pre>
15508      */
15509     getSortState : function(){
15510         return this.sortInfo;
15511     },
15512
15513     // private
15514     applySort : function(){
15515         if(this.sortInfo && !this.remoteSort){
15516             var s = this.sortInfo, f = s.field;
15517             var st = this.fields.get(f).sortType;
15518             var fn = function(r1, r2){
15519                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15520                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15521             };
15522             this.data.sort(s.direction, fn);
15523             if(this.snapshot && this.snapshot != this.data){
15524                 this.snapshot.sort(s.direction, fn);
15525             }
15526         }
15527     },
15528
15529     /**
15530      * Sets the default sort column and order to be used by the next load operation.
15531      * @param {String} fieldName The name of the field to sort by.
15532      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15533      */
15534     setDefaultSort : function(field, dir){
15535         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15536     },
15537
15538     /**
15539      * Sort the Records.
15540      * If remote sorting is used, the sort is performed on the server, and the cache is
15541      * reloaded. If local sorting is used, the cache is sorted internally.
15542      * @param {String} fieldName The name of the field to sort by.
15543      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15544      */
15545     sort : function(fieldName, dir){
15546         var f = this.fields.get(fieldName);
15547         if(!dir){
15548             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15549             
15550             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15551                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15552             }else{
15553                 dir = f.sortDir;
15554             }
15555         }
15556         this.sortToggle[f.name] = dir;
15557         this.sortInfo = {field: f.name, direction: dir};
15558         if(!this.remoteSort){
15559             this.applySort();
15560             this.fireEvent("datachanged", this);
15561         }else{
15562             this.load(this.lastOptions);
15563         }
15564     },
15565
15566     /**
15567      * Calls the specified function for each of the Records in the cache.
15568      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15569      * Returning <em>false</em> aborts and exits the iteration.
15570      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15571      */
15572     each : function(fn, scope){
15573         this.data.each(fn, scope);
15574     },
15575
15576     /**
15577      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15578      * (e.g., during paging).
15579      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15580      */
15581     getModifiedRecords : function(){
15582         return this.modified;
15583     },
15584
15585     // private
15586     createFilterFn : function(property, value, anyMatch){
15587         if(!value.exec){ // not a regex
15588             value = String(value);
15589             if(value.length == 0){
15590                 return false;
15591             }
15592             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15593         }
15594         return function(r){
15595             return value.test(r.data[property]);
15596         };
15597     },
15598
15599     /**
15600      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15601      * @param {String} property A field on your records
15602      * @param {Number} start The record index to start at (defaults to 0)
15603      * @param {Number} end The last record index to include (defaults to length - 1)
15604      * @return {Number} The sum
15605      */
15606     sum : function(property, start, end){
15607         var rs = this.data.items, v = 0;
15608         start = start || 0;
15609         end = (end || end === 0) ? end : rs.length-1;
15610
15611         for(var i = start; i <= end; i++){
15612             v += (rs[i].data[property] || 0);
15613         }
15614         return v;
15615     },
15616
15617     /**
15618      * Filter the records by a specified property.
15619      * @param {String} field A field on your records
15620      * @param {String/RegExp} value Either a string that the field
15621      * should start with or a RegExp to test against the field
15622      * @param {Boolean} anyMatch True to match any part not just the beginning
15623      */
15624     filter : function(property, value, anyMatch){
15625         var fn = this.createFilterFn(property, value, anyMatch);
15626         return fn ? this.filterBy(fn) : this.clearFilter();
15627     },
15628
15629     /**
15630      * Filter by a function. The specified function will be called with each
15631      * record in this data source. If the function returns true the record is included,
15632      * otherwise it is filtered.
15633      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15634      * @param {Object} scope (optional) The scope of the function (defaults to this)
15635      */
15636     filterBy : function(fn, scope){
15637         this.snapshot = this.snapshot || this.data;
15638         this.data = this.queryBy(fn, scope||this);
15639         this.fireEvent("datachanged", this);
15640     },
15641
15642     /**
15643      * Query the records by a specified property.
15644      * @param {String} field A field on your records
15645      * @param {String/RegExp} value Either a string that the field
15646      * should start with or a RegExp to test against the field
15647      * @param {Boolean} anyMatch True to match any part not just the beginning
15648      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15649      */
15650     query : function(property, value, anyMatch){
15651         var fn = this.createFilterFn(property, value, anyMatch);
15652         return fn ? this.queryBy(fn) : this.data.clone();
15653     },
15654
15655     /**
15656      * Query by a function. The specified function will be called with each
15657      * record in this data source. If the function returns true the record is included
15658      * in the results.
15659      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15660      * @param {Object} scope (optional) The scope of the function (defaults to this)
15661       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15662      **/
15663     queryBy : function(fn, scope){
15664         var data = this.snapshot || this.data;
15665         return data.filterBy(fn, scope||this);
15666     },
15667
15668     /**
15669      * Collects unique values for a particular dataIndex from this store.
15670      * @param {String} dataIndex The property to collect
15671      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15672      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15673      * @return {Array} An array of the unique values
15674      **/
15675     collect : function(dataIndex, allowNull, bypassFilter){
15676         var d = (bypassFilter === true && this.snapshot) ?
15677                 this.snapshot.items : this.data.items;
15678         var v, sv, r = [], l = {};
15679         for(var i = 0, len = d.length; i < len; i++){
15680             v = d[i].data[dataIndex];
15681             sv = String(v);
15682             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15683                 l[sv] = true;
15684                 r[r.length] = v;
15685             }
15686         }
15687         return r;
15688     },
15689
15690     /**
15691      * Revert to a view of the Record cache with no filtering applied.
15692      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15693      */
15694     clearFilter : function(suppressEvent){
15695         if(this.snapshot && this.snapshot != this.data){
15696             this.data = this.snapshot;
15697             delete this.snapshot;
15698             if(suppressEvent !== true){
15699                 this.fireEvent("datachanged", this);
15700             }
15701         }
15702     },
15703
15704     // private
15705     afterEdit : function(record){
15706         if(this.modified.indexOf(record) == -1){
15707             this.modified.push(record);
15708         }
15709         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15710     },
15711     
15712     // private
15713     afterReject : function(record){
15714         this.modified.remove(record);
15715         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15716     },
15717
15718     // private
15719     afterCommit : function(record){
15720         this.modified.remove(record);
15721         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15722     },
15723
15724     /**
15725      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15726      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15727      */
15728     commitChanges : function(){
15729         var m = this.modified.slice(0);
15730         this.modified = [];
15731         for(var i = 0, len = m.length; i < len; i++){
15732             m[i].commit();
15733         }
15734     },
15735
15736     /**
15737      * Cancel outstanding changes on all changed records.
15738      */
15739     rejectChanges : function(){
15740         var m = this.modified.slice(0);
15741         this.modified = [];
15742         for(var i = 0, len = m.length; i < len; i++){
15743             m[i].reject();
15744         }
15745     },
15746
15747     onMetaChange : function(meta, rtype, o){
15748         this.recordType = rtype;
15749         this.fields = rtype.prototype.fields;
15750         delete this.snapshot;
15751         this.sortInfo = meta.sortInfo || this.sortInfo;
15752         this.modified = [];
15753         this.fireEvent('metachange', this, this.reader.meta);
15754     },
15755     
15756     moveIndex : function(data, type)
15757     {
15758         var index = this.indexOf(data);
15759         
15760         var newIndex = index + type;
15761         
15762         this.remove(data);
15763         
15764         this.insert(newIndex, data);
15765         
15766     }
15767 });/*
15768  * Based on:
15769  * Ext JS Library 1.1.1
15770  * Copyright(c) 2006-2007, Ext JS, LLC.
15771  *
15772  * Originally Released Under LGPL - original licence link has changed is not relivant.
15773  *
15774  * Fork - LGPL
15775  * <script type="text/javascript">
15776  */
15777
15778 /**
15779  * @class Roo.data.SimpleStore
15780  * @extends Roo.data.Store
15781  * Small helper class to make creating Stores from Array data easier.
15782  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15783  * @cfg {Array} fields An array of field definition objects, or field name strings.
15784  * @cfg {Object} an existing reader (eg. copied from another store)
15785  * @cfg {Array} data The multi-dimensional array of data
15786  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15787  * @cfg {Roo.data.Reader} reader  [not-required] 
15788  * @constructor
15789  * @param {Object} config
15790  */
15791 Roo.data.SimpleStore = function(config)
15792 {
15793     Roo.data.SimpleStore.superclass.constructor.call(this, {
15794         isLocal : true,
15795         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15796                 id: config.id
15797             },
15798             Roo.data.Record.create(config.fields)
15799         ),
15800         proxy : new Roo.data.MemoryProxy(config.data)
15801     });
15802     this.load();
15803 };
15804 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15805  * Based on:
15806  * Ext JS Library 1.1.1
15807  * Copyright(c) 2006-2007, Ext JS, LLC.
15808  *
15809  * Originally Released Under LGPL - original licence link has changed is not relivant.
15810  *
15811  * Fork - LGPL
15812  * <script type="text/javascript">
15813  */
15814
15815 /**
15816 /**
15817  * @extends Roo.data.Store
15818  * @class Roo.data.JsonStore
15819  * Small helper class to make creating Stores for JSON data easier. <br/>
15820 <pre><code>
15821 var store = new Roo.data.JsonStore({
15822     url: 'get-images.php',
15823     root: 'images',
15824     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15825 });
15826 </code></pre>
15827  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15828  * JsonReader and HttpProxy (unless inline data is provided).</b>
15829  * @cfg {Array} fields An array of field definition objects, or field name strings.
15830  * @constructor
15831  * @param {Object} config
15832  */
15833 Roo.data.JsonStore = function(c){
15834     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15835         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15836         reader: new Roo.data.JsonReader(c, c.fields)
15837     }));
15838 };
15839 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15840  * Based on:
15841  * Ext JS Library 1.1.1
15842  * Copyright(c) 2006-2007, Ext JS, LLC.
15843  *
15844  * Originally Released Under LGPL - original licence link has changed is not relivant.
15845  *
15846  * Fork - LGPL
15847  * <script type="text/javascript">
15848  */
15849
15850  
15851 Roo.data.Field = function(config){
15852     if(typeof config == "string"){
15853         config = {name: config};
15854     }
15855     Roo.apply(this, config);
15856     
15857     if(!this.type){
15858         this.type = "auto";
15859     }
15860     
15861     var st = Roo.data.SortTypes;
15862     // named sortTypes are supported, here we look them up
15863     if(typeof this.sortType == "string"){
15864         this.sortType = st[this.sortType];
15865     }
15866     
15867     // set default sortType for strings and dates
15868     if(!this.sortType){
15869         switch(this.type){
15870             case "string":
15871                 this.sortType = st.asUCString;
15872                 break;
15873             case "date":
15874                 this.sortType = st.asDate;
15875                 break;
15876             default:
15877                 this.sortType = st.none;
15878         }
15879     }
15880
15881     // define once
15882     var stripRe = /[\$,%]/g;
15883
15884     // prebuilt conversion function for this field, instead of
15885     // switching every time we're reading a value
15886     if(!this.convert){
15887         var cv, dateFormat = this.dateFormat;
15888         switch(this.type){
15889             case "":
15890             case "auto":
15891             case undefined:
15892                 cv = function(v){ return v; };
15893                 break;
15894             case "string":
15895                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15896                 break;
15897             case "int":
15898                 cv = function(v){
15899                     return v !== undefined && v !== null && v !== '' ?
15900                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15901                     };
15902                 break;
15903             case "float":
15904                 cv = function(v){
15905                     return v !== undefined && v !== null && v !== '' ?
15906                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15907                     };
15908                 break;
15909             case "bool":
15910             case "boolean":
15911                 cv = function(v){ return v === true || v === "true" || v == 1; };
15912                 break;
15913             case "date":
15914                 cv = function(v){
15915                     if(!v){
15916                         return '';
15917                     }
15918                     if(v instanceof Date){
15919                         return v;
15920                     }
15921                     if(dateFormat){
15922                         if(dateFormat == "timestamp"){
15923                             return new Date(v*1000);
15924                         }
15925                         return Date.parseDate(v, dateFormat);
15926                     }
15927                     var parsed = Date.parse(v);
15928                     return parsed ? new Date(parsed) : null;
15929                 };
15930              break;
15931             
15932         }
15933         this.convert = cv;
15934     }
15935 };
15936
15937 Roo.data.Field.prototype = {
15938     dateFormat: null,
15939     defaultValue: "",
15940     mapping: null,
15941     sortType : null,
15942     sortDir : "ASC"
15943 };/*
15944  * Based on:
15945  * Ext JS Library 1.1.1
15946  * Copyright(c) 2006-2007, Ext JS, LLC.
15947  *
15948  * Originally Released Under LGPL - original licence link has changed is not relivant.
15949  *
15950  * Fork - LGPL
15951  * <script type="text/javascript">
15952  */
15953  
15954 // Base class for reading structured data from a data source.  This class is intended to be
15955 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15956
15957 /**
15958  * @class Roo.data.DataReader
15959  * @abstract
15960  * Base class for reading structured data from a data source.  This class is intended to be
15961  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15962  */
15963
15964 Roo.data.DataReader = function(meta, recordType){
15965     
15966     this.meta = meta;
15967     
15968     this.recordType = recordType instanceof Array ? 
15969         Roo.data.Record.create(recordType) : recordType;
15970 };
15971
15972 Roo.data.DataReader.prototype = {
15973     
15974     
15975     readerType : 'Data',
15976      /**
15977      * Create an empty record
15978      * @param {Object} data (optional) - overlay some values
15979      * @return {Roo.data.Record} record created.
15980      */
15981     newRow :  function(d) {
15982         var da =  {};
15983         this.recordType.prototype.fields.each(function(c) {
15984             switch( c.type) {
15985                 case 'int' : da[c.name] = 0; break;
15986                 case 'date' : da[c.name] = new Date(); break;
15987                 case 'float' : da[c.name] = 0.0; break;
15988                 case 'boolean' : da[c.name] = false; break;
15989                 default : da[c.name] = ""; break;
15990             }
15991             
15992         });
15993         return new this.recordType(Roo.apply(da, d));
15994     }
15995     
15996     
15997 };/*
15998  * Based on:
15999  * Ext JS Library 1.1.1
16000  * Copyright(c) 2006-2007, Ext JS, LLC.
16001  *
16002  * Originally Released Under LGPL - original licence link has changed is not relivant.
16003  *
16004  * Fork - LGPL
16005  * <script type="text/javascript">
16006  */
16007
16008 /**
16009  * @class Roo.data.DataProxy
16010  * @extends Roo.util.Observable
16011  * @abstract
16012  * This class is an abstract base class for implementations which provide retrieval of
16013  * unformatted data objects.<br>
16014  * <p>
16015  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16016  * (of the appropriate type which knows how to parse the data object) to provide a block of
16017  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16018  * <p>
16019  * Custom implementations must implement the load method as described in
16020  * {@link Roo.data.HttpProxy#load}.
16021  */
16022 Roo.data.DataProxy = function(){
16023     this.addEvents({
16024         /**
16025          * @event beforeload
16026          * Fires before a network request is made to retrieve a data object.
16027          * @param {Object} This DataProxy object.
16028          * @param {Object} params The params parameter to the load function.
16029          */
16030         beforeload : true,
16031         /**
16032          * @event load
16033          * Fires before the load method's callback is called.
16034          * @param {Object} This DataProxy object.
16035          * @param {Object} o The data object.
16036          * @param {Object} arg The callback argument object passed to the load function.
16037          */
16038         load : true,
16039         /**
16040          * @event loadexception
16041          * Fires if an Exception occurs during data retrieval.
16042          * @param {Object} This DataProxy object.
16043          * @param {Object} o The data object.
16044          * @param {Object} arg The callback argument object passed to the load function.
16045          * @param {Object} e The Exception.
16046          */
16047         loadexception : true
16048     });
16049     Roo.data.DataProxy.superclass.constructor.call(this);
16050 };
16051
16052 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16053
16054     /**
16055      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16056      */
16057 /*
16058  * Based on:
16059  * Ext JS Library 1.1.1
16060  * Copyright(c) 2006-2007, Ext JS, LLC.
16061  *
16062  * Originally Released Under LGPL - original licence link has changed is not relivant.
16063  *
16064  * Fork - LGPL
16065  * <script type="text/javascript">
16066  */
16067 /**
16068  * @class Roo.data.MemoryProxy
16069  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16070  * to the Reader when its load method is called.
16071  * @constructor
16072  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16073  */
16074 Roo.data.MemoryProxy = function(data){
16075     if (data.data) {
16076         data = data.data;
16077     }
16078     Roo.data.MemoryProxy.superclass.constructor.call(this);
16079     this.data = data;
16080 };
16081
16082 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16083     
16084     /**
16085      * Load data from the requested source (in this case an in-memory
16086      * data object passed to the constructor), read the data object into
16087      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16088      * process that block using the passed callback.
16089      * @param {Object} params This parameter is not used by the MemoryProxy class.
16090      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16091      * object into a block of Roo.data.Records.
16092      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16093      * The function must be passed <ul>
16094      * <li>The Record block object</li>
16095      * <li>The "arg" argument from the load function</li>
16096      * <li>A boolean success indicator</li>
16097      * </ul>
16098      * @param {Object} scope The scope in which to call the callback
16099      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16100      */
16101     load : function(params, reader, callback, scope, arg){
16102         params = params || {};
16103         var result;
16104         try {
16105             result = reader.readRecords(params.data ? params.data :this.data);
16106         }catch(e){
16107             this.fireEvent("loadexception", this, arg, null, e);
16108             callback.call(scope, null, arg, false);
16109             return;
16110         }
16111         callback.call(scope, result, arg, true);
16112     },
16113     
16114     // private
16115     update : function(params, records){
16116         
16117     }
16118 });/*
16119  * Based on:
16120  * Ext JS Library 1.1.1
16121  * Copyright(c) 2006-2007, Ext JS, LLC.
16122  *
16123  * Originally Released Under LGPL - original licence link has changed is not relivant.
16124  *
16125  * Fork - LGPL
16126  * <script type="text/javascript">
16127  */
16128 /**
16129  * @class Roo.data.HttpProxy
16130  * @extends Roo.data.DataProxy
16131  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16132  * configured to reference a certain URL.<br><br>
16133  * <p>
16134  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16135  * from which the running page was served.<br><br>
16136  * <p>
16137  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16138  * <p>
16139  * Be aware that to enable the browser to parse an XML document, the server must set
16140  * the Content-Type header in the HTTP response to "text/xml".
16141  * @constructor
16142  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16143  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16144  * will be used to make the request.
16145  */
16146 Roo.data.HttpProxy = function(conn){
16147     Roo.data.HttpProxy.superclass.constructor.call(this);
16148     // is conn a conn config or a real conn?
16149     this.conn = conn;
16150     this.useAjax = !conn || !conn.events;
16151   
16152 };
16153
16154 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16155     // thse are take from connection...
16156     
16157     /**
16158      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16159      */
16160     /**
16161      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16162      * extra parameters to each request made by this object. (defaults to undefined)
16163      */
16164     /**
16165      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16166      *  to each request made by this object. (defaults to undefined)
16167      */
16168     /**
16169      * @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)
16170      */
16171     /**
16172      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16173      */
16174      /**
16175      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16176      * @type Boolean
16177      */
16178   
16179
16180     /**
16181      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16182      * @type Boolean
16183      */
16184     /**
16185      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16186      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16187      * a finer-grained basis than the DataProxy events.
16188      */
16189     getConnection : function(){
16190         return this.useAjax ? Roo.Ajax : this.conn;
16191     },
16192
16193     /**
16194      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16195      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16196      * process that block using the passed callback.
16197      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16198      * for the request to the remote server.
16199      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16200      * object into a block of Roo.data.Records.
16201      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16202      * The function must be passed <ul>
16203      * <li>The Record block object</li>
16204      * <li>The "arg" argument from the load function</li>
16205      * <li>A boolean success indicator</li>
16206      * </ul>
16207      * @param {Object} scope The scope in which to call the callback
16208      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16209      */
16210     load : function(params, reader, callback, scope, arg){
16211         if(this.fireEvent("beforeload", this, params) !== false){
16212             var  o = {
16213                 params : params || {},
16214                 request: {
16215                     callback : callback,
16216                     scope : scope,
16217                     arg : arg
16218                 },
16219                 reader: reader,
16220                 callback : this.loadResponse,
16221                 scope: this
16222             };
16223             if(this.useAjax){
16224                 Roo.applyIf(o, this.conn);
16225                 if(this.activeRequest){
16226                     Roo.Ajax.abort(this.activeRequest);
16227                 }
16228                 this.activeRequest = Roo.Ajax.request(o);
16229             }else{
16230                 this.conn.request(o);
16231             }
16232         }else{
16233             callback.call(scope||this, null, arg, false);
16234         }
16235     },
16236
16237     // private
16238     loadResponse : function(o, success, response){
16239         delete this.activeRequest;
16240         if(!success){
16241             this.fireEvent("loadexception", this, o, response);
16242             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16243             return;
16244         }
16245         var result;
16246         try {
16247             result = o.reader.read(response);
16248         }catch(e){
16249             o.success = false;
16250             o.raw = { errorMsg : response.responseText };
16251             this.fireEvent("loadexception", this, o, response, e);
16252             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16253             return;
16254         }
16255         
16256         this.fireEvent("load", this, o, o.request.arg);
16257         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16258     },
16259
16260     // private
16261     update : function(dataSet){
16262
16263     },
16264
16265     // private
16266     updateResponse : function(dataSet){
16267
16268     }
16269 });/*
16270  * Based on:
16271  * Ext JS Library 1.1.1
16272  * Copyright(c) 2006-2007, Ext JS, LLC.
16273  *
16274  * Originally Released Under LGPL - original licence link has changed is not relivant.
16275  *
16276  * Fork - LGPL
16277  * <script type="text/javascript">
16278  */
16279
16280 /**
16281  * @class Roo.data.ScriptTagProxy
16282  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16283  * other than the originating domain of the running page.<br><br>
16284  * <p>
16285  * <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
16286  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16287  * <p>
16288  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16289  * source code that is used as the source inside a &lt;script> tag.<br><br>
16290  * <p>
16291  * In order for the browser to process the returned data, the server must wrap the data object
16292  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16293  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16294  * depending on whether the callback name was passed:
16295  * <p>
16296  * <pre><code>
16297 boolean scriptTag = false;
16298 String cb = request.getParameter("callback");
16299 if (cb != null) {
16300     scriptTag = true;
16301     response.setContentType("text/javascript");
16302 } else {
16303     response.setContentType("application/x-json");
16304 }
16305 Writer out = response.getWriter();
16306 if (scriptTag) {
16307     out.write(cb + "(");
16308 }
16309 out.print(dataBlock.toJsonString());
16310 if (scriptTag) {
16311     out.write(");");
16312 }
16313 </pre></code>
16314  *
16315  * @constructor
16316  * @param {Object} config A configuration object.
16317  */
16318 Roo.data.ScriptTagProxy = function(config){
16319     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16320     Roo.apply(this, config);
16321     this.head = document.getElementsByTagName("head")[0];
16322 };
16323
16324 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16325
16326 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16327     /**
16328      * @cfg {String} url The URL from which to request the data object.
16329      */
16330     /**
16331      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16332      */
16333     timeout : 30000,
16334     /**
16335      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16336      * the server the name of the callback function set up by the load call to process the returned data object.
16337      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16338      * javascript output which calls this named function passing the data object as its only parameter.
16339      */
16340     callbackParam : "callback",
16341     /**
16342      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16343      * name to the request.
16344      */
16345     nocache : true,
16346
16347     /**
16348      * Load data from the configured URL, read the data object into
16349      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16350      * process that block using the passed callback.
16351      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16352      * for the request to the remote server.
16353      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16354      * object into a block of Roo.data.Records.
16355      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16356      * The function must be passed <ul>
16357      * <li>The Record block object</li>
16358      * <li>The "arg" argument from the load function</li>
16359      * <li>A boolean success indicator</li>
16360      * </ul>
16361      * @param {Object} scope The scope in which to call the callback
16362      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16363      */
16364     load : function(params, reader, callback, scope, arg){
16365         if(this.fireEvent("beforeload", this, params) !== false){
16366
16367             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16368
16369             var url = this.url;
16370             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16371             if(this.nocache){
16372                 url += "&_dc=" + (new Date().getTime());
16373             }
16374             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16375             var trans = {
16376                 id : transId,
16377                 cb : "stcCallback"+transId,
16378                 scriptId : "stcScript"+transId,
16379                 params : params,
16380                 arg : arg,
16381                 url : url,
16382                 callback : callback,
16383                 scope : scope,
16384                 reader : reader
16385             };
16386             var conn = this;
16387
16388             window[trans.cb] = function(o){
16389                 conn.handleResponse(o, trans);
16390             };
16391
16392             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16393
16394             if(this.autoAbort !== false){
16395                 this.abort();
16396             }
16397
16398             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16399
16400             var script = document.createElement("script");
16401             script.setAttribute("src", url);
16402             script.setAttribute("type", "text/javascript");
16403             script.setAttribute("id", trans.scriptId);
16404             this.head.appendChild(script);
16405
16406             this.trans = trans;
16407         }else{
16408             callback.call(scope||this, null, arg, false);
16409         }
16410     },
16411
16412     // private
16413     isLoading : function(){
16414         return this.trans ? true : false;
16415     },
16416
16417     /**
16418      * Abort the current server request.
16419      */
16420     abort : function(){
16421         if(this.isLoading()){
16422             this.destroyTrans(this.trans);
16423         }
16424     },
16425
16426     // private
16427     destroyTrans : function(trans, isLoaded){
16428         this.head.removeChild(document.getElementById(trans.scriptId));
16429         clearTimeout(trans.timeoutId);
16430         if(isLoaded){
16431             window[trans.cb] = undefined;
16432             try{
16433                 delete window[trans.cb];
16434             }catch(e){}
16435         }else{
16436             // if hasn't been loaded, wait for load to remove it to prevent script error
16437             window[trans.cb] = function(){
16438                 window[trans.cb] = undefined;
16439                 try{
16440                     delete window[trans.cb];
16441                 }catch(e){}
16442             };
16443         }
16444     },
16445
16446     // private
16447     handleResponse : function(o, trans){
16448         this.trans = false;
16449         this.destroyTrans(trans, true);
16450         var result;
16451         try {
16452             result = trans.reader.readRecords(o);
16453         }catch(e){
16454             this.fireEvent("loadexception", this, o, trans.arg, e);
16455             trans.callback.call(trans.scope||window, null, trans.arg, false);
16456             return;
16457         }
16458         this.fireEvent("load", this, o, trans.arg);
16459         trans.callback.call(trans.scope||window, result, trans.arg, true);
16460     },
16461
16462     // private
16463     handleFailure : function(trans){
16464         this.trans = false;
16465         this.destroyTrans(trans, false);
16466         this.fireEvent("loadexception", this, null, trans.arg);
16467         trans.callback.call(trans.scope||window, null, trans.arg, false);
16468     }
16469 });/*
16470  * Based on:
16471  * Ext JS Library 1.1.1
16472  * Copyright(c) 2006-2007, Ext JS, LLC.
16473  *
16474  * Originally Released Under LGPL - original licence link has changed is not relivant.
16475  *
16476  * Fork - LGPL
16477  * <script type="text/javascript">
16478  */
16479
16480 /**
16481  * @class Roo.data.JsonReader
16482  * @extends Roo.data.DataReader
16483  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16484  * based on mappings in a provided Roo.data.Record constructor.
16485  * 
16486  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16487  * in the reply previously. 
16488  * 
16489  * <p>
16490  * Example code:
16491  * <pre><code>
16492 var RecordDef = Roo.data.Record.create([
16493     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16494     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16495 ]);
16496 var myReader = new Roo.data.JsonReader({
16497     totalProperty: "results",    // The property which contains the total dataset size (optional)
16498     root: "rows",                // The property which contains an Array of row objects
16499     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16500 }, RecordDef);
16501 </code></pre>
16502  * <p>
16503  * This would consume a JSON file like this:
16504  * <pre><code>
16505 { 'results': 2, 'rows': [
16506     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16507     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16508 }
16509 </code></pre>
16510  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16511  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16512  * paged from the remote server.
16513  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16514  * @cfg {String} root name of the property which contains the Array of row objects.
16515  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16516  * @cfg {Array} fields Array of field definition objects
16517  * @constructor
16518  * Create a new JsonReader
16519  * @param {Object} meta Metadata configuration options
16520  * @param {Object} recordType Either an Array of field definition objects,
16521  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16522  */
16523 Roo.data.JsonReader = function(meta, recordType){
16524     
16525     meta = meta || {};
16526     // set some defaults:
16527     Roo.applyIf(meta, {
16528         totalProperty: 'total',
16529         successProperty : 'success',
16530         root : 'data',
16531         id : 'id'
16532     });
16533     
16534     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16535 };
16536 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16537     
16538     readerType : 'Json',
16539     
16540     /**
16541      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16542      * Used by Store query builder to append _requestMeta to params.
16543      * 
16544      */
16545     metaFromRemote : false,
16546     /**
16547      * This method is only used by a DataProxy which has retrieved data from a remote server.
16548      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16549      * @return {Object} data A data block which is used by an Roo.data.Store object as
16550      * a cache of Roo.data.Records.
16551      */
16552     read : function(response){
16553         var json = response.responseText;
16554        
16555         var o = /* eval:var:o */ eval("("+json+")");
16556         if(!o) {
16557             throw {message: "JsonReader.read: Json object not found"};
16558         }
16559         
16560         if(o.metaData){
16561             
16562             delete this.ef;
16563             this.metaFromRemote = true;
16564             this.meta = o.metaData;
16565             this.recordType = Roo.data.Record.create(o.metaData.fields);
16566             this.onMetaChange(this.meta, this.recordType, o);
16567         }
16568         return this.readRecords(o);
16569     },
16570
16571     // private function a store will implement
16572     onMetaChange : function(meta, recordType, o){
16573
16574     },
16575
16576     /**
16577          * @ignore
16578          */
16579     simpleAccess: function(obj, subsc) {
16580         return obj[subsc];
16581     },
16582
16583         /**
16584          * @ignore
16585          */
16586     getJsonAccessor: function(){
16587         var re = /[\[\.]/;
16588         return function(expr) {
16589             try {
16590                 return(re.test(expr))
16591                     ? new Function("obj", "return obj." + expr)
16592                     : function(obj){
16593                         return obj[expr];
16594                     };
16595             } catch(e){}
16596             return Roo.emptyFn;
16597         };
16598     }(),
16599
16600     /**
16601      * Create a data block containing Roo.data.Records from an XML document.
16602      * @param {Object} o An object which contains an Array of row objects in the property specified
16603      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16604      * which contains the total size of the dataset.
16605      * @return {Object} data A data block which is used by an Roo.data.Store object as
16606      * a cache of Roo.data.Records.
16607      */
16608     readRecords : function(o){
16609         /**
16610          * After any data loads, the raw JSON data is available for further custom processing.
16611          * @type Object
16612          */
16613         this.o = o;
16614         var s = this.meta, Record = this.recordType,
16615             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16616
16617 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16618         if (!this.ef) {
16619             if(s.totalProperty) {
16620                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16621                 }
16622                 if(s.successProperty) {
16623                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16624                 }
16625                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16626                 if (s.id) {
16627                         var g = this.getJsonAccessor(s.id);
16628                         this.getId = function(rec) {
16629                                 var r = g(rec);  
16630                                 return (r === undefined || r === "") ? null : r;
16631                         };
16632                 } else {
16633                         this.getId = function(){return null;};
16634                 }
16635             this.ef = [];
16636             for(var jj = 0; jj < fl; jj++){
16637                 f = fi[jj];
16638                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16639                 this.ef[jj] = this.getJsonAccessor(map);
16640             }
16641         }
16642
16643         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16644         if(s.totalProperty){
16645             var vt = parseInt(this.getTotal(o), 10);
16646             if(!isNaN(vt)){
16647                 totalRecords = vt;
16648             }
16649         }
16650         if(s.successProperty){
16651             var vs = this.getSuccess(o);
16652             if(vs === false || vs === 'false'){
16653                 success = false;
16654             }
16655         }
16656         var records = [];
16657         for(var i = 0; i < c; i++){
16658             var n = root[i];
16659             var values = {};
16660             var id = this.getId(n);
16661             for(var j = 0; j < fl; j++){
16662                 f = fi[j];
16663                                 var v = this.ef[j](n);
16664                                 if (!f.convert) {
16665                                         Roo.log('missing convert for ' + f.name);
16666                                         Roo.log(f);
16667                                         continue;
16668                                 }
16669                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16670             }
16671                         if (!Record) {
16672                                 return {
16673                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16674                                         success : false,
16675                                         records : [],
16676                                         totalRecords : 0
16677                                 };
16678                         }
16679             var record = new Record(values, id);
16680             record.json = n;
16681             records[i] = record;
16682         }
16683         return {
16684             raw : o,
16685             success : success,
16686             records : records,
16687             totalRecords : totalRecords
16688         };
16689     },
16690     // used when loading children.. @see loadDataFromChildren
16691     toLoadData: function(rec)
16692     {
16693         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16694         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16695         return { data : data, total : data.length };
16696         
16697     }
16698 });/*
16699  * Based on:
16700  * Ext JS Library 1.1.1
16701  * Copyright(c) 2006-2007, Ext JS, LLC.
16702  *
16703  * Originally Released Under LGPL - original licence link has changed is not relivant.
16704  *
16705  * Fork - LGPL
16706  * <script type="text/javascript">
16707  */
16708
16709 /**
16710  * @class Roo.data.ArrayReader
16711  * @extends Roo.data.DataReader
16712  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16713  * Each element of that Array represents a row of data fields. The
16714  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16715  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16716  * <p>
16717  * Example code:.
16718  * <pre><code>
16719 var RecordDef = Roo.data.Record.create([
16720     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16721     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16722 ]);
16723 var myReader = new Roo.data.ArrayReader({
16724     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16725 }, RecordDef);
16726 </code></pre>
16727  * <p>
16728  * This would consume an Array like this:
16729  * <pre><code>
16730 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16731   </code></pre>
16732  
16733  * @constructor
16734  * Create a new JsonReader
16735  * @param {Object} meta Metadata configuration options.
16736  * @param {Object|Array} recordType Either an Array of field definition objects
16737  * 
16738  * @cfg {Array} fields Array of field definition objects
16739  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16740  * as specified to {@link Roo.data.Record#create},
16741  * or an {@link Roo.data.Record} object
16742  *
16743  * 
16744  * created using {@link Roo.data.Record#create}.
16745  */
16746 Roo.data.ArrayReader = function(meta, recordType)
16747 {    
16748     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16749 };
16750
16751 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16752     
16753       /**
16754      * Create a data block containing Roo.data.Records from an XML document.
16755      * @param {Object} o An Array of row objects which represents the dataset.
16756      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16757      * a cache of Roo.data.Records.
16758      */
16759     readRecords : function(o)
16760     {
16761         var sid = this.meta ? this.meta.id : null;
16762         var recordType = this.recordType, fields = recordType.prototype.fields;
16763         var records = [];
16764         var root = o;
16765         for(var i = 0; i < root.length; i++){
16766             var n = root[i];
16767             var values = {};
16768             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16769             for(var j = 0, jlen = fields.length; j < jlen; j++){
16770                 var f = fields.items[j];
16771                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16772                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16773                 v = f.convert(v);
16774                 values[f.name] = v;
16775             }
16776             var record = new recordType(values, id);
16777             record.json = n;
16778             records[records.length] = record;
16779         }
16780         return {
16781             records : records,
16782             totalRecords : records.length
16783         };
16784     },
16785     // used when loading children.. @see loadDataFromChildren
16786     toLoadData: function(rec)
16787     {
16788         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16789         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16790         
16791     }
16792     
16793     
16794 });/*
16795  * - LGPL
16796  * * 
16797  */
16798
16799 /**
16800  * @class Roo.bootstrap.form.ComboBox
16801  * @extends Roo.bootstrap.form.TriggerField
16802  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16803  * @cfg {Boolean} append (true|false) default false
16804  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16805  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16806  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16807  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16808  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16809  * @cfg {Boolean} animate default true
16810  * @cfg {Boolean} emptyResultText only for touch device
16811  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16812  * @cfg {String} emptyTitle default ''
16813  * @cfg {Number} width fixed with? experimental
16814  * @constructor
16815  * Create a new ComboBox.
16816  * @param {Object} config Configuration options
16817  */
16818 Roo.bootstrap.form.ComboBox = function(config){
16819     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16820     this.addEvents({
16821         /**
16822          * @event expand
16823          * Fires when the dropdown list is expanded
16824         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16825         */
16826         'expand' : true,
16827         /**
16828          * @event collapse
16829          * Fires when the dropdown list is collapsed
16830         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16831         */
16832         'collapse' : true,
16833         /**
16834          * @event beforeselect
16835          * Fires before a list item is selected. Return false to cancel the selection.
16836         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16837         * @param {Roo.data.Record} record The data record returned from the underlying store
16838         * @param {Number} index The index of the selected item in the dropdown list
16839         */
16840         'beforeselect' : true,
16841         /**
16842          * @event select
16843          * Fires when a list item is selected
16844         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16845         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16846         * @param {Number} index The index of the selected item in the dropdown list
16847         */
16848         'select' : true,
16849         /**
16850          * @event beforequery
16851          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16852          * The event object passed has these properties:
16853         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16854         * @param {String} query The query
16855         * @param {Boolean} forceAll true to force "all" query
16856         * @param {Boolean} cancel true to cancel the query
16857         * @param {Object} e The query event object
16858         */
16859         'beforequery': true,
16860          /**
16861          * @event add
16862          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16863         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16864         */
16865         'add' : true,
16866         /**
16867          * @event edit
16868          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16869         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16870         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16871         */
16872         'edit' : true,
16873         /**
16874          * @event remove
16875          * Fires when the remove value from the combobox array
16876         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877         */
16878         'remove' : true,
16879         /**
16880          * @event afterremove
16881          * Fires when the remove value from the combobox array
16882         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16883         */
16884         'afterremove' : true,
16885         /**
16886          * @event specialfilter
16887          * Fires when specialfilter
16888             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16889             */
16890         'specialfilter' : true,
16891         /**
16892          * @event tick
16893          * Fires when tick the element
16894             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16895             */
16896         'tick' : true,
16897         /**
16898          * @event touchviewdisplay
16899          * Fires when touch view require special display (default is using displayField)
16900             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16901             * @param {Object} cfg set html .
16902             */
16903         'touchviewdisplay' : true
16904         
16905     });
16906     
16907     this.item = [];
16908     this.tickItems = [];
16909     
16910     this.selectedIndex = -1;
16911     if(this.mode == 'local'){
16912         if(config.queryDelay === undefined){
16913             this.queryDelay = 10;
16914         }
16915         if(config.minChars === undefined){
16916             this.minChars = 0;
16917         }
16918     }
16919 };
16920
16921 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16922      
16923     /**
16924      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16925      * rendering into an Roo.Editor, defaults to false)
16926      */
16927     /**
16928      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16929      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16930      */
16931     /**
16932      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16933      */
16934     /**
16935      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16936      * the dropdown list (defaults to undefined, with no header element)
16937      */
16938
16939      /**
16940      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16941      */
16942      
16943      /**
16944      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16945      */
16946     listWidth: undefined,
16947     /**
16948      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16949      * mode = 'remote' or 'text' if mode = 'local')
16950      */
16951     displayField: undefined,
16952     
16953     /**
16954      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16955      * mode = 'remote' or 'value' if mode = 'local'). 
16956      * Note: use of a valueField requires the user make a selection
16957      * in order for a value to be mapped.
16958      */
16959     valueField: undefined,
16960     /**
16961      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16962      */
16963     modalTitle : '',
16964     
16965     /**
16966      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16967      * field's data value (defaults to the underlying DOM element's name)
16968      */
16969     hiddenName: undefined,
16970     /**
16971      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16972      */
16973     listClass: '',
16974     /**
16975      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16976      */
16977     selectedClass: 'active',
16978     
16979     /**
16980      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16981      */
16982     shadow:'sides',
16983     /**
16984      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16985      * anchor positions (defaults to 'tl-bl')
16986      */
16987     listAlign: 'tl-bl?',
16988     /**
16989      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16990      */
16991     maxHeight: 300,
16992     /**
16993      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16994      * query specified by the allQuery config option (defaults to 'query')
16995      */
16996     triggerAction: 'query',
16997     /**
16998      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16999      * (defaults to 4, does not apply if editable = false)
17000      */
17001     minChars : 4,
17002     /**
17003      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17004      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17005      */
17006     typeAhead: false,
17007     /**
17008      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17009      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17010      */
17011     queryDelay: 500,
17012     /**
17013      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17014      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17015      */
17016     pageSize: 0,
17017     /**
17018      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17019      * when editable = true (defaults to false)
17020      */
17021     selectOnFocus:false,
17022     /**
17023      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17024      */
17025     queryParam: 'query',
17026     /**
17027      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17028      * when mode = 'remote' (defaults to 'Loading...')
17029      */
17030     loadingText: 'Loading...',
17031     /**
17032      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17033      */
17034     resizable: false,
17035     /**
17036      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17037      */
17038     handleHeight : 8,
17039     /**
17040      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17041      * traditional select (defaults to true)
17042      */
17043     editable: true,
17044     /**
17045      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17046      */
17047     allQuery: '',
17048     /**
17049      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17050      */
17051     mode: 'remote',
17052     /**
17053      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17054      * listWidth has a higher value)
17055      */
17056     minListWidth : 70,
17057     /**
17058      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17059      * allow the user to set arbitrary text into the field (defaults to false)
17060      */
17061     forceSelection:false,
17062     /**
17063      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17064      * if typeAhead = true (defaults to 250)
17065      */
17066     typeAheadDelay : 250,
17067     /**
17068      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17069      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17070      */
17071     valueNotFoundText : undefined,
17072     /**
17073      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17074      */
17075     blockFocus : false,
17076     
17077     /**
17078      * @cfg {Boolean} disableClear Disable showing of clear button.
17079      */
17080     disableClear : false,
17081     /**
17082      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17083      */
17084     alwaysQuery : false,
17085     
17086     /**
17087      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17088      */
17089     multiple : false,
17090     
17091     /**
17092      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17093      */
17094     invalidClass : "has-warning",
17095     
17096     /**
17097      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17098      */
17099     validClass : "has-success",
17100     
17101     /**
17102      * @cfg {Boolean} specialFilter (true|false) special filter default false
17103      */
17104     specialFilter : false,
17105     
17106     /**
17107      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17108      */
17109     mobileTouchView : true,
17110     
17111     /**
17112      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17113      */
17114     useNativeIOS : false,
17115     
17116     /**
17117      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17118      */
17119     mobile_restrict_height : false,
17120     
17121     ios_options : false,
17122     
17123     //private
17124     addicon : false,
17125     editicon: false,
17126     
17127     page: 0,
17128     hasQuery: false,
17129     append: false,
17130     loadNext: false,
17131     autoFocus : true,
17132     tickable : false,
17133     btnPosition : 'right',
17134     triggerList : true,
17135     showToggleBtn : true,
17136     animate : true,
17137     emptyResultText: 'Empty',
17138     triggerText : 'Select',
17139     emptyTitle : '',
17140     width : false,
17141     
17142     // element that contains real text value.. (when hidden is used..)
17143     
17144     getAutoCreate : function()
17145     {   
17146         var cfg = false;
17147         //render
17148         /*
17149          * Render classic select for iso
17150          */
17151         
17152         if(Roo.isIOS && this.useNativeIOS){
17153             cfg = this.getAutoCreateNativeIOS();
17154             return cfg;
17155         }
17156         
17157         /*
17158          * Touch Devices
17159          */
17160         
17161         if(Roo.isTouch && this.mobileTouchView){
17162             cfg = this.getAutoCreateTouchView();
17163             return cfg;;
17164         }
17165         
17166         /*
17167          *  Normal ComboBox
17168          */
17169         if(!this.tickable){
17170             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17171             return cfg;
17172         }
17173         
17174         /*
17175          *  ComboBox with tickable selections
17176          */
17177              
17178         var align = this.labelAlign || this.parentLabelAlign();
17179         
17180         cfg = {
17181             cls : 'form-group roo-combobox-tickable' //input-group
17182         };
17183         
17184         var btn_text_select = '';
17185         var btn_text_done = '';
17186         var btn_text_cancel = '';
17187         
17188         if (this.btn_text_show) {
17189             btn_text_select = 'Select';
17190             btn_text_done = 'Done';
17191             btn_text_cancel = 'Cancel'; 
17192         }
17193         
17194         var buttons = {
17195             tag : 'div',
17196             cls : 'tickable-buttons',
17197             cn : [
17198                 {
17199                     tag : 'button',
17200                     type : 'button',
17201                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17202                     //html : this.triggerText
17203                     html: btn_text_select
17204                 },
17205                 {
17206                     tag : 'button',
17207                     type : 'button',
17208                     name : 'ok',
17209                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17210                     //html : 'Done'
17211                     html: btn_text_done
17212                 },
17213                 {
17214                     tag : 'button',
17215                     type : 'button',
17216                     name : 'cancel',
17217                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17218                     //html : 'Cancel'
17219                     html: btn_text_cancel
17220                 }
17221             ]
17222         };
17223         
17224         if(this.editable){
17225             buttons.cn.unshift({
17226                 tag: 'input',
17227                 cls: 'roo-select2-search-field-input'
17228             });
17229         }
17230         
17231         var _this = this;
17232         
17233         Roo.each(buttons.cn, function(c){
17234             if (_this.size) {
17235                 c.cls += ' btn-' + _this.size;
17236             }
17237
17238             if (_this.disabled) {
17239                 c.disabled = true;
17240             }
17241         });
17242         
17243         var box = {
17244             tag: 'div',
17245             style : 'display: contents',
17246             cn: [
17247                 {
17248                     tag: 'input',
17249                     type : 'hidden',
17250                     cls: 'form-hidden-field'
17251                 },
17252                 {
17253                     tag: 'ul',
17254                     cls: 'roo-select2-choices',
17255                     cn:[
17256                         {
17257                             tag: 'li',
17258                             cls: 'roo-select2-search-field',
17259                             cn: [
17260                                 buttons
17261                             ]
17262                         }
17263                     ]
17264                 }
17265             ]
17266         };
17267         
17268         var combobox = {
17269             cls: 'roo-select2-container input-group roo-select2-container-multi',
17270             cn: [
17271                 
17272                 box
17273 //                {
17274 //                    tag: 'ul',
17275 //                    cls: 'typeahead typeahead-long dropdown-menu',
17276 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17277 //                }
17278             ]
17279         };
17280         
17281         if(this.hasFeedback && !this.allowBlank){
17282             
17283             var feedback = {
17284                 tag: 'span',
17285                 cls: 'glyphicon form-control-feedback'
17286             };
17287
17288             combobox.cn.push(feedback);
17289         }
17290         
17291         
17292         
17293         var indicator = {
17294             tag : 'i',
17295             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17296             tooltip : 'This field is required'
17297         };
17298         if (Roo.bootstrap.version == 4) {
17299             indicator = {
17300                 tag : 'i',
17301                 style : 'display:none'
17302             };
17303         }
17304         if (align ==='left' && this.fieldLabel.length) {
17305             
17306             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17307             
17308             cfg.cn = [
17309                 indicator,
17310                 {
17311                     tag: 'label',
17312                     'for' :  id,
17313                     cls : 'control-label col-form-label',
17314                     html : this.fieldLabel
17315
17316                 },
17317                 {
17318                     cls : "", 
17319                     cn: [
17320                         combobox
17321                     ]
17322                 }
17323
17324             ];
17325             
17326             var labelCfg = cfg.cn[1];
17327             var contentCfg = cfg.cn[2];
17328             
17329
17330             if(this.indicatorpos == 'right'){
17331                 
17332                 cfg.cn = [
17333                     {
17334                         tag: 'label',
17335                         'for' :  id,
17336                         cls : 'control-label col-form-label',
17337                         cn : [
17338                             {
17339                                 tag : 'span',
17340                                 html : this.fieldLabel
17341                             },
17342                             indicator
17343                         ]
17344                     },
17345                     {
17346                         cls : "",
17347                         cn: [
17348                             combobox
17349                         ]
17350                     }
17351
17352                 ];
17353                 
17354                 
17355                 
17356                 labelCfg = cfg.cn[0];
17357                 contentCfg = cfg.cn[1];
17358             
17359             }
17360             
17361             if(this.labelWidth > 12){
17362                 labelCfg.style = "width: " + this.labelWidth + 'px';
17363             }
17364             if(this.width * 1 > 0){
17365                 contentCfg.style = "width: " + this.width + 'px';
17366             }
17367             if(this.labelWidth < 13 && this.labelmd == 0){
17368                 this.labelmd = this.labelWidth;
17369             }
17370             
17371             if(this.labellg > 0){
17372                 labelCfg.cls += ' col-lg-' + this.labellg;
17373                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17374             }
17375             
17376             if(this.labelmd > 0){
17377                 labelCfg.cls += ' col-md-' + this.labelmd;
17378                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17379             }
17380             
17381             if(this.labelsm > 0){
17382                 labelCfg.cls += ' col-sm-' + this.labelsm;
17383                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17384             }
17385             
17386             if(this.labelxs > 0){
17387                 labelCfg.cls += ' col-xs-' + this.labelxs;
17388                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17389             }
17390                 
17391                 
17392         } else if ( this.fieldLabel.length) {
17393 //                Roo.log(" label");
17394                  cfg.cn = [
17395                    indicator,
17396                     {
17397                         tag: 'label',
17398                         //cls : 'input-group-addon',
17399                         html : this.fieldLabel
17400                     },
17401                     combobox
17402                 ];
17403                 
17404                 if(this.indicatorpos == 'right'){
17405                     cfg.cn = [
17406                         {
17407                             tag: 'label',
17408                             //cls : 'input-group-addon',
17409                             html : this.fieldLabel
17410                         },
17411                         indicator,
17412                         combobox
17413                     ];
17414                     
17415                 }
17416
17417         } else {
17418             
17419 //                Roo.log(" no label && no align");
17420                 cfg = combobox
17421                      
17422                 
17423         }
17424          
17425         var settings=this;
17426         ['xs','sm','md','lg'].map(function(size){
17427             if (settings[size]) {
17428                 cfg.cls += ' col-' + size + '-' + settings[size];
17429             }
17430         });
17431         
17432         return cfg;
17433         
17434     },
17435     
17436     _initEventsCalled : false,
17437     
17438     // private
17439     initEvents: function()
17440     {   
17441         if (this._initEventsCalled) { // as we call render... prevent looping...
17442             return;
17443         }
17444         this._initEventsCalled = true;
17445         
17446         if (!this.store) {
17447             throw "can not find store for combo";
17448         }
17449         
17450         this.indicator = this.indicatorEl();
17451         
17452         this.store = Roo.factory(this.store, Roo.data);
17453         this.store.parent = this;
17454         
17455         // if we are building from html. then this element is so complex, that we can not really
17456         // use the rendered HTML.
17457         // so we have to trash and replace the previous code.
17458         if (Roo.XComponent.build_from_html) {
17459             // remove this element....
17460             var e = this.el.dom, k=0;
17461             while (e ) { e = e.previousSibling;  ++k;}
17462
17463             this.el.remove();
17464             
17465             this.el=false;
17466             this.rendered = false;
17467             
17468             this.render(this.parent().getChildContainer(true), k);
17469         }
17470         
17471         if(Roo.isIOS && this.useNativeIOS){
17472             this.initIOSView();
17473             return;
17474         }
17475         
17476         /*
17477          * Touch Devices
17478          */
17479         
17480         if(Roo.isTouch && this.mobileTouchView){
17481             this.initTouchView();
17482             return;
17483         }
17484         
17485         if(this.tickable){
17486             this.initTickableEvents();
17487             return;
17488         }
17489         
17490         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17491         
17492         if(this.hiddenName){
17493             
17494             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17495             
17496             this.hiddenField.dom.value =
17497                 this.hiddenValue !== undefined ? this.hiddenValue :
17498                 this.value !== undefined ? this.value : '';
17499
17500             // prevent input submission
17501             this.el.dom.removeAttribute('name');
17502             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17503              
17504              
17505         }
17506         //if(Roo.isGecko){
17507         //    this.el.dom.setAttribute('autocomplete', 'off');
17508         //}
17509         
17510         var cls = 'x-combo-list';
17511         
17512         //this.list = new Roo.Layer({
17513         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17514         //});
17515         
17516         var _this = this;
17517         
17518         (function(){
17519             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17520             _this.list.setWidth(lw);
17521         }).defer(100);
17522         
17523         this.list.on('mouseover', this.onViewOver, this);
17524         this.list.on('mousemove', this.onViewMove, this);
17525         this.list.on('scroll', this.onViewScroll, this);
17526         
17527         /*
17528         this.list.swallowEvent('mousewheel');
17529         this.assetHeight = 0;
17530
17531         if(this.title){
17532             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17533             this.assetHeight += this.header.getHeight();
17534         }
17535
17536         this.innerList = this.list.createChild({cls:cls+'-inner'});
17537         this.innerList.on('mouseover', this.onViewOver, this);
17538         this.innerList.on('mousemove', this.onViewMove, this);
17539         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17540         
17541         if(this.allowBlank && !this.pageSize && !this.disableClear){
17542             this.footer = this.list.createChild({cls:cls+'-ft'});
17543             this.pageTb = new Roo.Toolbar(this.footer);
17544            
17545         }
17546         if(this.pageSize){
17547             this.footer = this.list.createChild({cls:cls+'-ft'});
17548             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17549                     {pageSize: this.pageSize});
17550             
17551         }
17552         
17553         if (this.pageTb && this.allowBlank && !this.disableClear) {
17554             var _this = this;
17555             this.pageTb.add(new Roo.Toolbar.Fill(), {
17556                 cls: 'x-btn-icon x-btn-clear',
17557                 text: '&#160;',
17558                 handler: function()
17559                 {
17560                     _this.collapse();
17561                     _this.clearValue();
17562                     _this.onSelect(false, -1);
17563                 }
17564             });
17565         }
17566         if (this.footer) {
17567             this.assetHeight += this.footer.getHeight();
17568         }
17569         */
17570             
17571         if(!this.tpl){
17572             this.tpl = Roo.bootstrap.version == 4 ?
17573                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17574                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17575         }
17576
17577         this.view = new Roo.View(this.list, this.tpl, {
17578             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17579         });
17580         //this.view.wrapEl.setDisplayed(false);
17581         this.view.on('click', this.onViewClick, this);
17582         
17583         
17584         this.store.on('beforeload', this.onBeforeLoad, this);
17585         this.store.on('load', this.onLoad, this);
17586         this.store.on('loadexception', this.onLoadException, this);
17587         /*
17588         if(this.resizable){
17589             this.resizer = new Roo.Resizable(this.list,  {
17590                pinned:true, handles:'se'
17591             });
17592             this.resizer.on('resize', function(r, w, h){
17593                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17594                 this.listWidth = w;
17595                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17596                 this.restrictHeight();
17597             }, this);
17598             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17599         }
17600         */
17601         if(!this.editable){
17602             this.editable = true;
17603             this.setEditable(false);
17604         }
17605         
17606         /*
17607         
17608         if (typeof(this.events.add.listeners) != 'undefined') {
17609             
17610             this.addicon = this.wrap.createChild(
17611                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17612        
17613             this.addicon.on('click', function(e) {
17614                 this.fireEvent('add', this);
17615             }, this);
17616         }
17617         if (typeof(this.events.edit.listeners) != 'undefined') {
17618             
17619             this.editicon = this.wrap.createChild(
17620                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17621             if (this.addicon) {
17622                 this.editicon.setStyle('margin-left', '40px');
17623             }
17624             this.editicon.on('click', function(e) {
17625                 
17626                 // we fire even  if inothing is selected..
17627                 this.fireEvent('edit', this, this.lastData );
17628                 
17629             }, this);
17630         }
17631         */
17632         
17633         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17634             "up" : function(e){
17635                 this.inKeyMode = true;
17636                 this.selectPrev();
17637             },
17638
17639             "down" : function(e){
17640                 if(!this.isExpanded()){
17641                     this.onTriggerClick();
17642                 }else{
17643                     this.inKeyMode = true;
17644                     this.selectNext();
17645                 }
17646             },
17647
17648             "enter" : function(e){
17649 //                this.onViewClick();
17650                 //return true;
17651                 this.collapse();
17652                 
17653                 if(this.fireEvent("specialkey", this, e)){
17654                     this.onViewClick(false);
17655                 }
17656                 
17657                 return true;
17658             },
17659
17660             "esc" : function(e){
17661                 this.collapse();
17662             },
17663
17664             "tab" : function(e){
17665                 this.collapse();
17666                 
17667                 if(this.fireEvent("specialkey", this, e)){
17668                     this.onViewClick(false);
17669                 }
17670                 
17671                 return true;
17672             },
17673
17674             scope : this,
17675
17676             doRelay : function(foo, bar, hname){
17677                 if(hname == 'down' || this.scope.isExpanded()){
17678                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17679                 }
17680                 return true;
17681             },
17682
17683             forceKeyDown: true
17684         });
17685         
17686         
17687         this.queryDelay = Math.max(this.queryDelay || 10,
17688                 this.mode == 'local' ? 10 : 250);
17689         
17690         
17691         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17692         
17693         if(this.typeAhead){
17694             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17695         }
17696         if(this.editable !== false){
17697             this.inputEl().on("keyup", this.onKeyUp, this);
17698         }
17699         if(this.forceSelection){
17700             this.inputEl().on('blur', this.doForce, this);
17701         }
17702         
17703         if(this.multiple){
17704             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17705             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17706         }
17707     },
17708     
17709     initTickableEvents: function()
17710     {   
17711         this.createList();
17712         
17713         if(this.hiddenName){
17714             
17715             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17716             
17717             this.hiddenField.dom.value =
17718                 this.hiddenValue !== undefined ? this.hiddenValue :
17719                 this.value !== undefined ? this.value : '';
17720
17721             // prevent input submission
17722             this.el.dom.removeAttribute('name');
17723             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17724              
17725              
17726         }
17727         
17728 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17729         
17730         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17731         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17732         if(this.triggerList){
17733             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17734         }
17735          
17736         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17737         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17738         
17739         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17740         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17741         
17742         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17743         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17744         
17745         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17746         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17747         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17748         
17749         this.okBtn.hide();
17750         this.cancelBtn.hide();
17751         
17752         var _this = this;
17753         
17754         (function(){
17755             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17756             _this.list.setWidth(lw);
17757         }).defer(100);
17758         
17759         this.list.on('mouseover', this.onViewOver, this);
17760         this.list.on('mousemove', this.onViewMove, this);
17761         
17762         this.list.on('scroll', this.onViewScroll, this);
17763         
17764         if(!this.tpl){
17765             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17766                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17767         }
17768
17769         this.view = new Roo.View(this.list, this.tpl, {
17770             singleSelect:true,
17771             tickable:true,
17772             parent:this,
17773             store: this.store,
17774             selectedClass: this.selectedClass
17775         });
17776         
17777         //this.view.wrapEl.setDisplayed(false);
17778         this.view.on('click', this.onViewClick, this);
17779         
17780         
17781         
17782         this.store.on('beforeload', this.onBeforeLoad, this);
17783         this.store.on('load', this.onLoad, this);
17784         this.store.on('loadexception', this.onLoadException, this);
17785         
17786         if(this.editable){
17787             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17788                 "up" : function(e){
17789                     this.inKeyMode = true;
17790                     this.selectPrev();
17791                 },
17792
17793                 "down" : function(e){
17794                     this.inKeyMode = true;
17795                     this.selectNext();
17796                 },
17797
17798                 "enter" : function(e){
17799                     if(this.fireEvent("specialkey", this, e)){
17800                         this.onViewClick(false);
17801                     }
17802                     
17803                     return true;
17804                 },
17805
17806                 "esc" : function(e){
17807                     this.onTickableFooterButtonClick(e, false, false);
17808                 },
17809
17810                 "tab" : function(e){
17811                     this.fireEvent("specialkey", this, e);
17812                     
17813                     this.onTickableFooterButtonClick(e, false, false);
17814                     
17815                     return true;
17816                 },
17817
17818                 scope : this,
17819
17820                 doRelay : function(e, fn, key){
17821                     if(this.scope.isExpanded()){
17822                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17823                     }
17824                     return true;
17825                 },
17826
17827                 forceKeyDown: true
17828             });
17829         }
17830         
17831         this.queryDelay = Math.max(this.queryDelay || 10,
17832                 this.mode == 'local' ? 10 : 250);
17833         
17834         
17835         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17836         
17837         if(this.typeAhead){
17838             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17839         }
17840         
17841         if(this.editable !== false){
17842             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17843         }
17844         
17845         this.indicator = this.indicatorEl();
17846         
17847         if(this.indicator){
17848             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17849             this.indicator.hide();
17850         }
17851         
17852     },
17853
17854     onDestroy : function(){
17855         if(this.view){
17856             this.view.setStore(null);
17857             this.view.el.removeAllListeners();
17858             this.view.el.remove();
17859             this.view.purgeListeners();
17860         }
17861         if(this.list){
17862             this.list.dom.innerHTML  = '';
17863         }
17864         
17865         if(this.store){
17866             this.store.un('beforeload', this.onBeforeLoad, this);
17867             this.store.un('load', this.onLoad, this);
17868             this.store.un('loadexception', this.onLoadException, this);
17869         }
17870         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17871     },
17872
17873     // private
17874     fireKey : function(e){
17875         if(e.isNavKeyPress() && !this.list.isVisible()){
17876             this.fireEvent("specialkey", this, e);
17877         }
17878     },
17879
17880     // private
17881     onResize: function(w, h)
17882     {
17883         
17884         
17885 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17886 //        
17887 //        if(typeof w != 'number'){
17888 //            // we do not handle it!?!?
17889 //            return;
17890 //        }
17891 //        var tw = this.trigger.getWidth();
17892 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17893 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17894 //        var x = w - tw;
17895 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17896 //            
17897 //        //this.trigger.setStyle('left', x+'px');
17898 //        
17899 //        if(this.list && this.listWidth === undefined){
17900 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17901 //            this.list.setWidth(lw);
17902 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17903 //        }
17904         
17905     
17906         
17907     },
17908
17909     /**
17910      * Allow or prevent the user from directly editing the field text.  If false is passed,
17911      * the user will only be able to select from the items defined in the dropdown list.  This method
17912      * is the runtime equivalent of setting the 'editable' config option at config time.
17913      * @param {Boolean} value True to allow the user to directly edit the field text
17914      */
17915     setEditable : function(value){
17916         if(value == this.editable){
17917             return;
17918         }
17919         this.editable = value;
17920         if(!value){
17921             this.inputEl().dom.setAttribute('readOnly', true);
17922             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17923             this.inputEl().addClass('x-combo-noedit');
17924         }else{
17925             this.inputEl().dom.removeAttribute('readOnly');
17926             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17927             this.inputEl().removeClass('x-combo-noedit');
17928         }
17929     },
17930
17931     // private
17932     
17933     onBeforeLoad : function(combo,opts){
17934         if(!this.hasFocus){
17935             return;
17936         }
17937          if (!opts.add) {
17938             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17939          }
17940         this.restrictHeight();
17941         this.selectedIndex = -1;
17942     },
17943
17944     // private
17945     onLoad : function(){
17946         
17947         this.hasQuery = false;
17948         
17949         if(!this.hasFocus){
17950             return;
17951         }
17952         
17953         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17954             this.loading.hide();
17955         }
17956         
17957         if(this.store.getCount() > 0){
17958             
17959             this.expand();
17960             this.restrictHeight();
17961             if(this.lastQuery == this.allQuery){
17962                 if(this.editable && !this.tickable){
17963                     this.inputEl().dom.select();
17964                 }
17965                 
17966                 if(
17967                     !this.selectByValue(this.value, true) &&
17968                     this.autoFocus && 
17969                     (
17970                         !this.store.lastOptions ||
17971                         typeof(this.store.lastOptions.add) == 'undefined' || 
17972                         this.store.lastOptions.add != true
17973                     )
17974                 ){
17975                     this.select(0, true);
17976                 }
17977             }else{
17978                 if(this.autoFocus){
17979                     this.selectNext();
17980                 }
17981                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17982                     this.taTask.delay(this.typeAheadDelay);
17983                 }
17984             }
17985         }else{
17986             this.onEmptyResults();
17987         }
17988         
17989         //this.el.focus();
17990     },
17991     // private
17992     onLoadException : function()
17993     {
17994         this.hasQuery = false;
17995         
17996         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17997             this.loading.hide();
17998         }
17999         
18000         if(this.tickable && this.editable){
18001             return;
18002         }
18003         
18004         this.collapse();
18005         // only causes errors at present
18006         //Roo.log(this.store.reader.jsonData);
18007         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18008             // fixme
18009             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18010         //}
18011         
18012         
18013     },
18014     // private
18015     onTypeAhead : function(){
18016         if(this.store.getCount() > 0){
18017             var r = this.store.getAt(0);
18018             var newValue = r.data[this.displayField];
18019             var len = newValue.length;
18020             var selStart = this.getRawValue().length;
18021             
18022             if(selStart != len){
18023                 this.setRawValue(newValue);
18024                 this.selectText(selStart, newValue.length);
18025             }
18026         }
18027     },
18028
18029     // private
18030     onSelect : function(record, index){
18031         
18032         if(this.fireEvent('beforeselect', this, record, index) !== false){
18033         
18034             this.setFromData(index > -1 ? record.data : false);
18035             
18036             this.collapse();
18037             this.fireEvent('select', this, record, index);
18038         }
18039     },
18040
18041     /**
18042      * Returns the currently selected field value or empty string if no value is set.
18043      * @return {String} value The selected value
18044      */
18045     getValue : function()
18046     {
18047         if(Roo.isIOS && this.useNativeIOS){
18048             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18049         }
18050         
18051         if(this.multiple){
18052             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18053         }
18054         
18055         if(this.valueField){
18056             return typeof this.value != 'undefined' ? this.value : '';
18057         }else{
18058             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18059         }
18060     },
18061     
18062     getRawValue : function()
18063     {
18064         if(Roo.isIOS && this.useNativeIOS){
18065             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18066         }
18067         
18068         var v = this.inputEl().getValue();
18069         
18070         return v;
18071     },
18072
18073     /**
18074      * Clears any text/value currently set in the field
18075      */
18076     clearValue : function(){
18077         
18078         if(this.hiddenField){
18079             this.hiddenField.dom.value = '';
18080         }
18081         this.value = '';
18082         this.setRawValue('');
18083         this.lastSelectionText = '';
18084         this.lastData = false;
18085         
18086         var close = this.closeTriggerEl();
18087         
18088         if(close){
18089             close.hide();
18090         }
18091         
18092         this.validate();
18093         
18094     },
18095
18096     /**
18097      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18098      * will be displayed in the field.  If the value does not match the data value of an existing item,
18099      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18100      * Otherwise the field will be blank (although the value will still be set).
18101      * @param {String} value The value to match
18102      */
18103     setValue : function(v)
18104     {
18105         if(Roo.isIOS && this.useNativeIOS){
18106             this.setIOSValue(v);
18107             return;
18108         }
18109         
18110         if(this.multiple){
18111             this.syncValue();
18112             return;
18113         }
18114         
18115         var text = v;
18116         if(this.valueField){
18117             var r = this.findRecord(this.valueField, v);
18118             if(r){
18119                 text = r.data[this.displayField];
18120             }else if(this.valueNotFoundText !== undefined){
18121                 text = this.valueNotFoundText;
18122             }
18123         }
18124         this.lastSelectionText = text;
18125         if(this.hiddenField){
18126             this.hiddenField.dom.value = v;
18127         }
18128         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18129         this.value = v;
18130         
18131         var close = this.closeTriggerEl();
18132         
18133         if(close){
18134             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18135         }
18136         
18137         this.validate();
18138     },
18139     /**
18140      * @property {Object} the last set data for the element
18141      */
18142     
18143     lastData : false,
18144     /**
18145      * Sets the value of the field based on a object which is related to the record format for the store.
18146      * @param {Object} value the value to set as. or false on reset?
18147      */
18148     setFromData : function(o){
18149         
18150         if(this.multiple){
18151             this.addItem(o);
18152             return;
18153         }
18154             
18155         var dv = ''; // display value
18156         var vv = ''; // value value..
18157         this.lastData = o;
18158         if (this.displayField) {
18159             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18160         } else {
18161             // this is an error condition!!!
18162             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18163         }
18164         
18165         if(this.valueField){
18166             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18167         }
18168         
18169         var close = this.closeTriggerEl();
18170         
18171         if(close){
18172             if(dv.length || vv * 1 > 0){
18173                 close.show() ;
18174                 this.blockFocus=true;
18175             } else {
18176                 close.hide();
18177             }             
18178         }
18179         
18180         if(this.hiddenField){
18181             this.hiddenField.dom.value = vv;
18182             
18183             this.lastSelectionText = dv;
18184             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18185             this.value = vv;
18186             return;
18187         }
18188         // no hidden field.. - we store the value in 'value', but still display
18189         // display field!!!!
18190         this.lastSelectionText = dv;
18191         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18192         this.value = vv;
18193         
18194         
18195         
18196     },
18197     // private
18198     reset : function(){
18199         // overridden so that last data is reset..
18200         
18201         if(this.multiple){
18202             this.clearItem();
18203             return;
18204         }
18205         
18206         this.setValue(this.originalValue);
18207         //this.clearInvalid();
18208         this.lastData = false;
18209         if (this.view) {
18210             this.view.clearSelections();
18211         }
18212         
18213         this.validate();
18214     },
18215     // private
18216     findRecord : function(prop, value){
18217         var record;
18218         if(this.store.getCount() > 0){
18219             this.store.each(function(r){
18220                 if(r.data[prop] == value){
18221                     record = r;
18222                     return false;
18223                 }
18224                 return true;
18225             });
18226         }
18227         return record;
18228     },
18229     
18230     getName: function()
18231     {
18232         // returns hidden if it's set..
18233         if (!this.rendered) {return ''};
18234         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18235         
18236     },
18237     // private
18238     onViewMove : function(e, t){
18239         this.inKeyMode = false;
18240     },
18241
18242     // private
18243     onViewOver : function(e, t){
18244         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18245             return;
18246         }
18247         var item = this.view.findItemFromChild(t);
18248         
18249         if(item){
18250             var index = this.view.indexOf(item);
18251             this.select(index, false);
18252         }
18253     },
18254
18255     // private
18256     onViewClick : function(view, doFocus, el, e)
18257     {
18258         var index = this.view.getSelectedIndexes()[0];
18259         
18260         var r = this.store.getAt(index);
18261         
18262         if(this.tickable){
18263             
18264             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18265                 return;
18266             }
18267             
18268             var rm = false;
18269             var _this = this;
18270             
18271             Roo.each(this.tickItems, function(v,k){
18272                 
18273                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18274                     Roo.log(v);
18275                     _this.tickItems.splice(k, 1);
18276                     
18277                     if(typeof(e) == 'undefined' && view == false){
18278                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18279                     }
18280                     
18281                     rm = true;
18282                     return;
18283                 }
18284             });
18285             
18286             if(rm){
18287                 return;
18288             }
18289             
18290             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18291                 this.tickItems.push(r.data);
18292             }
18293             
18294             if(typeof(e) == 'undefined' && view == false){
18295                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18296             }
18297                     
18298             return;
18299         }
18300         
18301         if(r){
18302             this.onSelect(r, index);
18303         }
18304         if(doFocus !== false && !this.blockFocus){
18305             this.inputEl().focus();
18306         }
18307     },
18308
18309     // private
18310     restrictHeight : function(){
18311         //this.innerList.dom.style.height = '';
18312         //var inner = this.innerList.dom;
18313         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18314         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18315         //this.list.beginUpdate();
18316         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18317         this.list.alignTo(this.inputEl(), this.listAlign);
18318         this.list.alignTo(this.inputEl(), this.listAlign);
18319         //this.list.endUpdate();
18320     },
18321
18322     // private
18323     onEmptyResults : function(){
18324         
18325         if(this.tickable && this.editable){
18326             this.hasFocus = false;
18327             this.restrictHeight();
18328             return;
18329         }
18330         
18331         this.collapse();
18332     },
18333
18334     /**
18335      * Returns true if the dropdown list is expanded, else false.
18336      */
18337     isExpanded : function(){
18338         return this.list.isVisible();
18339     },
18340
18341     /**
18342      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18343      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18344      * @param {String} value The data value of the item to select
18345      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18346      * selected item if it is not currently in view (defaults to true)
18347      * @return {Boolean} True if the value matched an item in the list, else false
18348      */
18349     selectByValue : function(v, scrollIntoView){
18350         if(v !== undefined && v !== null){
18351             var r = this.findRecord(this.valueField || this.displayField, v);
18352             if(r){
18353                 this.select(this.store.indexOf(r), scrollIntoView);
18354                 return true;
18355             }
18356         }
18357         return false;
18358     },
18359
18360     /**
18361      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18362      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18363      * @param {Number} index The zero-based index of the list item to select
18364      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18365      * selected item if it is not currently in view (defaults to true)
18366      */
18367     select : function(index, scrollIntoView){
18368         this.selectedIndex = index;
18369         this.view.select(index);
18370         if(scrollIntoView !== false){
18371             var el = this.view.getNode(index);
18372             /*
18373              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18374              */
18375             if(el){
18376                 this.list.scrollChildIntoView(el, false);
18377             }
18378         }
18379     },
18380
18381     // private
18382     selectNext : function(){
18383         var ct = this.store.getCount();
18384         if(ct > 0){
18385             if(this.selectedIndex == -1){
18386                 this.select(0);
18387             }else if(this.selectedIndex < ct-1){
18388                 this.select(this.selectedIndex+1);
18389             }
18390         }
18391     },
18392
18393     // private
18394     selectPrev : function(){
18395         var ct = this.store.getCount();
18396         if(ct > 0){
18397             if(this.selectedIndex == -1){
18398                 this.select(0);
18399             }else if(this.selectedIndex != 0){
18400                 this.select(this.selectedIndex-1);
18401             }
18402         }
18403     },
18404
18405     // private
18406     onKeyUp : function(e){
18407         if(this.editable !== false && !e.isSpecialKey()){
18408             this.lastKey = e.getKey();
18409             this.dqTask.delay(this.queryDelay);
18410         }
18411     },
18412
18413     // private
18414     validateBlur : function(){
18415         return !this.list || !this.list.isVisible();   
18416     },
18417
18418     // private
18419     initQuery : function(){
18420         
18421         var v = this.getRawValue();
18422         
18423         if(this.tickable && this.editable){
18424             v = this.tickableInputEl().getValue();
18425         }
18426         
18427         this.doQuery(v);
18428     },
18429
18430     // private
18431     doForce : function(){
18432         if(this.inputEl().dom.value.length > 0){
18433             this.inputEl().dom.value =
18434                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18435              
18436         }
18437     },
18438
18439     /**
18440      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18441      * query allowing the query action to be canceled if needed.
18442      * @param {String} query The SQL query to execute
18443      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18444      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18445      * saved in the current store (defaults to false)
18446      */
18447     doQuery : function(q, forceAll){
18448         
18449         if(q === undefined || q === null){
18450             q = '';
18451         }
18452         var qe = {
18453             query: q,
18454             forceAll: forceAll,
18455             combo: this,
18456             cancel:false
18457         };
18458         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18459             return false;
18460         }
18461         q = qe.query;
18462         
18463         forceAll = qe.forceAll;
18464         if(forceAll === true || (q.length >= this.minChars)){
18465             
18466             this.hasQuery = true;
18467             
18468             if(this.lastQuery != q || this.alwaysQuery){
18469                 this.lastQuery = q;
18470                 if(this.mode == 'local'){
18471                     this.selectedIndex = -1;
18472                     if(forceAll){
18473                         this.store.clearFilter();
18474                     }else{
18475                         
18476                         if(this.specialFilter){
18477                             this.fireEvent('specialfilter', this);
18478                             this.onLoad();
18479                             return;
18480                         }
18481                         
18482                         this.store.filter(this.displayField, q);
18483                     }
18484                     
18485                     this.store.fireEvent("datachanged", this.store);
18486                     
18487                     this.onLoad();
18488                     
18489                     
18490                 }else{
18491                     
18492                     this.store.baseParams[this.queryParam] = q;
18493                     
18494                     var options = {params : this.getParams(q)};
18495                     
18496                     if(this.loadNext){
18497                         options.add = true;
18498                         options.params.start = this.page * this.pageSize;
18499                     }
18500                     
18501                     this.store.load(options);
18502                     
18503                     /*
18504                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18505                      *  we should expand the list on onLoad
18506                      *  so command out it
18507                      */
18508 //                    this.expand();
18509                 }
18510             }else{
18511                 this.selectedIndex = -1;
18512                 this.onLoad();   
18513             }
18514         }
18515         
18516         this.loadNext = false;
18517     },
18518     
18519     // private
18520     getParams : function(q){
18521         var p = {};
18522         //p[this.queryParam] = q;
18523         
18524         if(this.pageSize){
18525             p.start = 0;
18526             p.limit = this.pageSize;
18527         }
18528         return p;
18529     },
18530
18531     /**
18532      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18533      */
18534     collapse : function(){
18535         if(!this.isExpanded()){
18536             return;
18537         }
18538         
18539         this.list.hide();
18540         
18541         this.hasFocus = false;
18542         
18543         if(this.tickable){
18544             this.okBtn.hide();
18545             this.cancelBtn.hide();
18546             this.trigger.show();
18547             
18548             if(this.editable){
18549                 this.tickableInputEl().dom.value = '';
18550                 this.tickableInputEl().blur();
18551             }
18552             
18553         }
18554         
18555         Roo.get(document).un('mousedown', this.collapseIf, this);
18556         Roo.get(document).un('mousewheel', this.collapseIf, this);
18557         if (!this.editable) {
18558             Roo.get(document).un('keydown', this.listKeyPress, this);
18559         }
18560         this.fireEvent('collapse', this);
18561         
18562         this.validate();
18563     },
18564
18565     // private
18566     collapseIf : function(e){
18567         var in_combo  = e.within(this.el);
18568         var in_list =  e.within(this.list);
18569         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18570         
18571         if (in_combo || in_list || is_list) {
18572             //e.stopPropagation();
18573             return;
18574         }
18575         
18576         if(this.tickable){
18577             this.onTickableFooterButtonClick(e, false, false);
18578         }
18579
18580         this.collapse();
18581         
18582     },
18583
18584     /**
18585      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18586      */
18587     expand : function(){
18588        
18589         if(this.isExpanded() || !this.hasFocus){
18590             return;
18591         }
18592         
18593         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18594         this.list.setWidth(lw);
18595         
18596         Roo.log('expand');
18597         
18598         this.list.show();
18599         
18600         this.restrictHeight();
18601         
18602         if(this.tickable){
18603             
18604             this.tickItems = Roo.apply([], this.item);
18605             
18606             this.okBtn.show();
18607             this.cancelBtn.show();
18608             this.trigger.hide();
18609             
18610             if(this.editable){
18611                 this.tickableInputEl().focus();
18612             }
18613             
18614         }
18615         
18616         Roo.get(document).on('mousedown', this.collapseIf, this);
18617         Roo.get(document).on('mousewheel', this.collapseIf, this);
18618         if (!this.editable) {
18619             Roo.get(document).on('keydown', this.listKeyPress, this);
18620         }
18621         
18622         this.fireEvent('expand', this);
18623     },
18624
18625     // private
18626     // Implements the default empty TriggerField.onTriggerClick function
18627     onTriggerClick : function(e)
18628     {
18629         Roo.log('trigger click');
18630         
18631         if(this.disabled || !this.triggerList){
18632             return;
18633         }
18634         
18635         this.page = 0;
18636         this.loadNext = false;
18637         
18638         if(this.isExpanded()){
18639             this.collapse();
18640             if (!this.blockFocus) {
18641                 this.inputEl().focus();
18642             }
18643             
18644         }else {
18645             this.hasFocus = true;
18646             if(this.triggerAction == 'all') {
18647                 this.doQuery(this.allQuery, true);
18648             } else {
18649                 this.doQuery(this.getRawValue());
18650             }
18651             if (!this.blockFocus) {
18652                 this.inputEl().focus();
18653             }
18654         }
18655     },
18656     
18657     onTickableTriggerClick : function(e)
18658     {
18659         if(this.disabled){
18660             return;
18661         }
18662         
18663         this.page = 0;
18664         this.loadNext = false;
18665         this.hasFocus = true;
18666         
18667         if(this.triggerAction == 'all') {
18668             this.doQuery(this.allQuery, true);
18669         } else {
18670             this.doQuery(this.getRawValue());
18671         }
18672     },
18673     
18674     onSearchFieldClick : function(e)
18675     {
18676         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18677             this.onTickableFooterButtonClick(e, false, false);
18678             return;
18679         }
18680         
18681         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18682             return;
18683         }
18684         
18685         this.page = 0;
18686         this.loadNext = false;
18687         this.hasFocus = true;
18688         
18689         if(this.triggerAction == 'all') {
18690             this.doQuery(this.allQuery, true);
18691         } else {
18692             this.doQuery(this.getRawValue());
18693         }
18694     },
18695     
18696     listKeyPress : function(e)
18697     {
18698         //Roo.log('listkeypress');
18699         // scroll to first matching element based on key pres..
18700         if (e.isSpecialKey()) {
18701             return false;
18702         }
18703         var k = String.fromCharCode(e.getKey()).toUpperCase();
18704         //Roo.log(k);
18705         var match  = false;
18706         var csel = this.view.getSelectedNodes();
18707         var cselitem = false;
18708         if (csel.length) {
18709             var ix = this.view.indexOf(csel[0]);
18710             cselitem  = this.store.getAt(ix);
18711             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18712                 cselitem = false;
18713             }
18714             
18715         }
18716         
18717         this.store.each(function(v) { 
18718             if (cselitem) {
18719                 // start at existing selection.
18720                 if (cselitem.id == v.id) {
18721                     cselitem = false;
18722                 }
18723                 return true;
18724             }
18725                 
18726             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18727                 match = this.store.indexOf(v);
18728                 return false;
18729             }
18730             return true;
18731         }, this);
18732         
18733         if (match === false) {
18734             return true; // no more action?
18735         }
18736         // scroll to?
18737         this.view.select(match);
18738         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18739         sn.scrollIntoView(sn.dom.parentNode, false);
18740     },
18741     
18742     onViewScroll : function(e, t){
18743         
18744         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){
18745             return;
18746         }
18747         
18748         this.hasQuery = true;
18749         
18750         this.loading = this.list.select('.loading', true).first();
18751         
18752         if(this.loading === null){
18753             this.list.createChild({
18754                 tag: 'div',
18755                 cls: 'loading roo-select2-more-results roo-select2-active',
18756                 html: 'Loading more results...'
18757             });
18758             
18759             this.loading = this.list.select('.loading', true).first();
18760             
18761             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18762             
18763             this.loading.hide();
18764         }
18765         
18766         this.loading.show();
18767         
18768         var _combo = this;
18769         
18770         this.page++;
18771         this.loadNext = true;
18772         
18773         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18774         
18775         return;
18776     },
18777     
18778     addItem : function(o)
18779     {   
18780         var dv = ''; // display value
18781         
18782         if (this.displayField) {
18783             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18784         } else {
18785             // this is an error condition!!!
18786             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18787         }
18788         
18789         if(!dv.length){
18790             return;
18791         }
18792         
18793         var choice = this.choices.createChild({
18794             tag: 'li',
18795             cls: 'roo-select2-search-choice',
18796             cn: [
18797                 {
18798                     tag: 'div',
18799                     html: dv
18800                 },
18801                 {
18802                     tag: 'a',
18803                     href: '#',
18804                     cls: 'roo-select2-search-choice-close fa fa-times',
18805                     tabindex: '-1'
18806                 }
18807             ]
18808             
18809         }, this.searchField);
18810         
18811         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18812         
18813         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18814         
18815         this.item.push(o);
18816         
18817         this.lastData = o;
18818         
18819         this.syncValue();
18820         
18821         this.inputEl().dom.value = '';
18822         
18823         this.validate();
18824     },
18825     
18826     onRemoveItem : function(e, _self, o)
18827     {
18828         e.preventDefault();
18829         
18830         this.lastItem = Roo.apply([], this.item);
18831         
18832         var index = this.item.indexOf(o.data) * 1;
18833         
18834         if( index < 0){
18835             Roo.log('not this item?!');
18836             return;
18837         }
18838         
18839         this.item.splice(index, 1);
18840         o.item.remove();
18841         
18842         this.syncValue();
18843         
18844         this.fireEvent('remove', this, e);
18845         
18846         this.validate();
18847         
18848     },
18849     
18850     syncValue : function()
18851     {
18852         if(!this.item.length){
18853             this.clearValue();
18854             return;
18855         }
18856             
18857         var value = [];
18858         var _this = this;
18859         Roo.each(this.item, function(i){
18860             if(_this.valueField){
18861                 value.push(i[_this.valueField]);
18862                 return;
18863             }
18864
18865             value.push(i);
18866         });
18867
18868         this.value = value.join(',');
18869
18870         if(this.hiddenField){
18871             this.hiddenField.dom.value = this.value;
18872         }
18873         
18874         this.store.fireEvent("datachanged", this.store);
18875         
18876         this.validate();
18877     },
18878     
18879     clearItem : function()
18880     {
18881         if(!this.multiple){
18882             return;
18883         }
18884         
18885         this.item = [];
18886         
18887         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18888            c.remove();
18889         });
18890         
18891         this.syncValue();
18892         
18893         this.validate();
18894         
18895         if(this.tickable && !Roo.isTouch){
18896             this.view.refresh();
18897         }
18898     },
18899     
18900     inputEl: function ()
18901     {
18902         if(Roo.isIOS && this.useNativeIOS){
18903             return this.el.select('select.roo-ios-select', true).first();
18904         }
18905         
18906         if(Roo.isTouch && this.mobileTouchView){
18907             return this.el.select('input.form-control',true).first();
18908         }
18909         
18910         if(this.tickable){
18911             return this.searchField;
18912         }
18913         
18914         return this.el.select('input.form-control',true).first();
18915     },
18916     
18917     onTickableFooterButtonClick : function(e, btn, el)
18918     {
18919         e.preventDefault();
18920         
18921         this.lastItem = Roo.apply([], this.item);
18922         
18923         if(btn && btn.name == 'cancel'){
18924             this.tickItems = Roo.apply([], this.item);
18925             this.collapse();
18926             return;
18927         }
18928         
18929         this.clearItem();
18930         
18931         var _this = this;
18932         
18933         Roo.each(this.tickItems, function(o){
18934             _this.addItem(o);
18935         });
18936         
18937         this.collapse();
18938         
18939     },
18940     
18941     validate : function()
18942     {
18943         if(this.getVisibilityEl().hasClass('hidden')){
18944             return true;
18945         }
18946         
18947         var v = this.getRawValue();
18948         
18949         if(this.multiple){
18950             v = this.getValue();
18951         }
18952         
18953         if(this.disabled || this.allowBlank || v.length){
18954             this.markValid();
18955             return true;
18956         }
18957         
18958         this.markInvalid();
18959         return false;
18960     },
18961     
18962     tickableInputEl : function()
18963     {
18964         if(!this.tickable || !this.editable){
18965             return this.inputEl();
18966         }
18967         
18968         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18969     },
18970     
18971     
18972     getAutoCreateTouchView : function()
18973     {
18974         var id = Roo.id();
18975         
18976         var cfg = {
18977             cls: 'form-group' //input-group
18978         };
18979         
18980         var input =  {
18981             tag: 'input',
18982             id : id,
18983             type : this.inputType,
18984             cls : 'form-control x-combo-noedit',
18985             autocomplete: 'new-password',
18986             placeholder : this.placeholder || '',
18987             readonly : true
18988         };
18989         
18990         if (this.name) {
18991             input.name = this.name;
18992         }
18993         
18994         if (this.size) {
18995             input.cls += ' input-' + this.size;
18996         }
18997         
18998         if (this.disabled) {
18999             input.disabled = true;
19000         }
19001         
19002         var inputblock = {
19003             cls : 'roo-combobox-wrap',
19004             cn : [
19005                 input
19006             ]
19007         };
19008         
19009         if(this.before){
19010             inputblock.cls += ' input-group';
19011             
19012             inputblock.cn.unshift({
19013                 tag :'span',
19014                 cls : 'input-group-addon input-group-prepend input-group-text',
19015                 html : this.before
19016             });
19017         }
19018         
19019         if(this.removable && !this.multiple){
19020             inputblock.cls += ' roo-removable';
19021             
19022             inputblock.cn.push({
19023                 tag: 'button',
19024                 html : 'x',
19025                 cls : 'roo-combo-removable-btn close'
19026             });
19027         }
19028
19029         if(this.hasFeedback && !this.allowBlank){
19030             
19031             inputblock.cls += ' has-feedback';
19032             
19033             inputblock.cn.push({
19034                 tag: 'span',
19035                 cls: 'glyphicon form-control-feedback'
19036             });
19037             
19038         }
19039         
19040         if (this.after) {
19041             
19042             inputblock.cls += (this.before) ? '' : ' input-group';
19043             
19044             inputblock.cn.push({
19045                 tag :'span',
19046                 cls : 'input-group-addon input-group-append input-group-text',
19047                 html : this.after
19048             });
19049         }
19050
19051         
19052         var ibwrap = inputblock;
19053         
19054         if(this.multiple){
19055             ibwrap = {
19056                 tag: 'ul',
19057                 cls: 'roo-select2-choices',
19058                 cn:[
19059                     {
19060                         tag: 'li',
19061                         cls: 'roo-select2-search-field',
19062                         cn: [
19063
19064                             inputblock
19065                         ]
19066                     }
19067                 ]
19068             };
19069         
19070             
19071         }
19072         
19073         var combobox = {
19074             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19075             cn: [
19076                 {
19077                     tag: 'input',
19078                     type : 'hidden',
19079                     cls: 'form-hidden-field'
19080                 },
19081                 ibwrap
19082             ]
19083         };
19084         
19085         if(!this.multiple && this.showToggleBtn){
19086             
19087             var caret = {
19088                 cls: 'caret'
19089             };
19090             
19091             if (this.caret != false) {
19092                 caret = {
19093                      tag: 'i',
19094                      cls: 'fa fa-' + this.caret
19095                 };
19096                 
19097             }
19098             
19099             combobox.cn.push({
19100                 tag :'span',
19101                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19102                 cn : [
19103                     Roo.bootstrap.version == 3 ? caret : '',
19104                     {
19105                         tag: 'span',
19106                         cls: 'combobox-clear',
19107                         cn  : [
19108                             {
19109                                 tag : 'i',
19110                                 cls: 'icon-remove'
19111                             }
19112                         ]
19113                     }
19114                 ]
19115
19116             })
19117         }
19118         
19119         if(this.multiple){
19120             combobox.cls += ' roo-select2-container-multi';
19121         }
19122         
19123         var required =  this.allowBlank ?  {
19124                     tag : 'i',
19125                     style: 'display: none'
19126                 } : {
19127                    tag : 'i',
19128                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19129                    tooltip : 'This field is required'
19130                 };
19131         
19132         var align = this.labelAlign || this.parentLabelAlign();
19133         
19134         if (align ==='left' && this.fieldLabel.length) {
19135
19136             cfg.cn = [
19137                 required,
19138                 {
19139                     tag: 'label',
19140                     cls : 'control-label col-form-label',
19141                     html : this.fieldLabel
19142
19143                 },
19144                 {
19145                     cls : 'roo-combobox-wrap ', 
19146                     cn: [
19147                         combobox
19148                     ]
19149                 }
19150             ];
19151             
19152             var labelCfg = cfg.cn[1];
19153             var contentCfg = cfg.cn[2];
19154             
19155
19156             if(this.indicatorpos == 'right'){
19157                 cfg.cn = [
19158                     {
19159                         tag: 'label',
19160                         'for' :  id,
19161                         cls : 'control-label col-form-label',
19162                         cn : [
19163                             {
19164                                 tag : 'span',
19165                                 html : this.fieldLabel
19166                             },
19167                             required
19168                         ]
19169                     },
19170                     {
19171                         cls : "roo-combobox-wrap ",
19172                         cn: [
19173                             combobox
19174                         ]
19175                     }
19176
19177                 ];
19178                 
19179                 labelCfg = cfg.cn[0];
19180                 contentCfg = cfg.cn[1];
19181             }
19182             
19183            
19184             
19185             if(this.labelWidth > 12){
19186                 labelCfg.style = "width: " + this.labelWidth + 'px';
19187             }
19188            
19189             if(this.labelWidth < 13 && this.labelmd == 0){
19190                 this.labelmd = this.labelWidth;
19191             }
19192             
19193             if(this.labellg > 0){
19194                 labelCfg.cls += ' col-lg-' + this.labellg;
19195                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19196             }
19197             
19198             if(this.labelmd > 0){
19199                 labelCfg.cls += ' col-md-' + this.labelmd;
19200                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19201             }
19202             
19203             if(this.labelsm > 0){
19204                 labelCfg.cls += ' col-sm-' + this.labelsm;
19205                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19206             }
19207             
19208             if(this.labelxs > 0){
19209                 labelCfg.cls += ' col-xs-' + this.labelxs;
19210                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19211             }
19212                 
19213                 
19214         } else if ( this.fieldLabel.length) {
19215             cfg.cn = [
19216                required,
19217                 {
19218                     tag: 'label',
19219                     cls : 'control-label',
19220                     html : this.fieldLabel
19221
19222                 },
19223                 {
19224                     cls : '', 
19225                     cn: [
19226                         combobox
19227                     ]
19228                 }
19229             ];
19230             
19231             if(this.indicatorpos == 'right'){
19232                 cfg.cn = [
19233                     {
19234                         tag: 'label',
19235                         cls : 'control-label',
19236                         html : this.fieldLabel,
19237                         cn : [
19238                             required
19239                         ]
19240                     },
19241                     {
19242                         cls : '', 
19243                         cn: [
19244                             combobox
19245                         ]
19246                     }
19247                 ];
19248             }
19249         } else {
19250             cfg.cn = combobox;    
19251         }
19252         
19253         
19254         var settings = this;
19255         
19256         ['xs','sm','md','lg'].map(function(size){
19257             if (settings[size]) {
19258                 cfg.cls += ' col-' + size + '-' + settings[size];
19259             }
19260         });
19261         
19262         return cfg;
19263     },
19264     
19265     initTouchView : function()
19266     {
19267         this.renderTouchView();
19268         
19269         this.touchViewEl.on('scroll', function(){
19270             this.el.dom.scrollTop = 0;
19271         }, this);
19272         
19273         this.originalValue = this.getValue();
19274         
19275         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19276         
19277         this.inputEl().on("click", this.showTouchView, this);
19278         if (this.triggerEl) {
19279             this.triggerEl.on("click", this.showTouchView, this);
19280         }
19281         
19282         
19283         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19284         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19285         
19286         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19287         
19288         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19289         this.store.on('load', this.onTouchViewLoad, this);
19290         this.store.on('loadexception', this.onTouchViewLoadException, this);
19291         
19292         if(this.hiddenName){
19293             
19294             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19295             
19296             this.hiddenField.dom.value =
19297                 this.hiddenValue !== undefined ? this.hiddenValue :
19298                 this.value !== undefined ? this.value : '';
19299         
19300             this.el.dom.removeAttribute('name');
19301             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19302         }
19303         
19304         if(this.multiple){
19305             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19306             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19307         }
19308         
19309         if(this.removable && !this.multiple){
19310             var close = this.closeTriggerEl();
19311             if(close){
19312                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19313                 close.on('click', this.removeBtnClick, this, close);
19314             }
19315         }
19316         /*
19317          * fix the bug in Safari iOS8
19318          */
19319         this.inputEl().on("focus", function(e){
19320             document.activeElement.blur();
19321         }, this);
19322         
19323         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19324         
19325         return;
19326         
19327         
19328     },
19329     
19330     renderTouchView : function()
19331     {
19332         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19333         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19334         
19335         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19336         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19337         
19338         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19339         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19340         this.touchViewBodyEl.setStyle('overflow', 'auto');
19341         
19342         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19343         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19344         
19345         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19346         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19347         
19348     },
19349     
19350     showTouchView : function()
19351     {
19352         if(this.disabled){
19353             return;
19354         }
19355         
19356         this.touchViewHeaderEl.hide();
19357
19358         if(this.modalTitle.length){
19359             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19360             this.touchViewHeaderEl.show();
19361         }
19362
19363         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19364         this.touchViewEl.show();
19365
19366         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19367         
19368         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19369         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19370
19371         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19372
19373         if(this.modalTitle.length){
19374             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19375         }
19376         
19377         this.touchViewBodyEl.setHeight(bodyHeight);
19378
19379         if(this.animate){
19380             var _this = this;
19381             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19382         }else{
19383             this.touchViewEl.addClass(['in','show']);
19384         }
19385         
19386         if(this._touchViewMask){
19387             Roo.get(document.body).addClass("x-body-masked");
19388             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19389             this._touchViewMask.setStyle('z-index', 10000);
19390             this._touchViewMask.addClass('show');
19391         }
19392         
19393         this.doTouchViewQuery();
19394         
19395     },
19396     
19397     hideTouchView : function()
19398     {
19399         this.touchViewEl.removeClass(['in','show']);
19400
19401         if(this.animate){
19402             var _this = this;
19403             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19404         }else{
19405             this.touchViewEl.setStyle('display', 'none');
19406         }
19407         
19408         if(this._touchViewMask){
19409             this._touchViewMask.removeClass('show');
19410             Roo.get(document.body).removeClass("x-body-masked");
19411         }
19412     },
19413     
19414     setTouchViewValue : function()
19415     {
19416         if(this.multiple){
19417             this.clearItem();
19418         
19419             var _this = this;
19420
19421             Roo.each(this.tickItems, function(o){
19422                 this.addItem(o);
19423             }, this);
19424         }
19425         
19426         this.hideTouchView();
19427     },
19428     
19429     doTouchViewQuery : function()
19430     {
19431         var qe = {
19432             query: '',
19433             forceAll: true,
19434             combo: this,
19435             cancel:false
19436         };
19437         
19438         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19439             return false;
19440         }
19441         
19442         if(!this.alwaysQuery || this.mode == 'local'){
19443             this.onTouchViewLoad();
19444             return;
19445         }
19446         
19447         this.store.load();
19448     },
19449     
19450     onTouchViewBeforeLoad : function(combo,opts)
19451     {
19452         return;
19453     },
19454
19455     // private
19456     onTouchViewLoad : function()
19457     {
19458         if(this.store.getCount() < 1){
19459             this.onTouchViewEmptyResults();
19460             return;
19461         }
19462         
19463         this.clearTouchView();
19464         
19465         var rawValue = this.getRawValue();
19466         
19467         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19468         
19469         this.tickItems = [];
19470         
19471         this.store.data.each(function(d, rowIndex){
19472             var row = this.touchViewListGroup.createChild(template);
19473             
19474             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19475                 row.addClass(d.data.cls);
19476             }
19477             
19478             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19479                 var cfg = {
19480                     data : d.data,
19481                     html : d.data[this.displayField]
19482                 };
19483                 
19484                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19485                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19486                 }
19487             }
19488             row.removeClass('selected');
19489             if(!this.multiple && this.valueField &&
19490                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19491             {
19492                 // radio buttons..
19493                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19494                 row.addClass('selected');
19495             }
19496             
19497             if(this.multiple && this.valueField &&
19498                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19499             {
19500                 
19501                 // checkboxes...
19502                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19503                 this.tickItems.push(d.data);
19504             }
19505             
19506             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19507             
19508         }, this);
19509         
19510         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19511         
19512         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19513
19514         if(this.modalTitle.length){
19515             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19516         }
19517
19518         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19519         
19520         if(this.mobile_restrict_height && listHeight < bodyHeight){
19521             this.touchViewBodyEl.setHeight(listHeight);
19522         }
19523         
19524         var _this = this;
19525         
19526         if(firstChecked && listHeight > bodyHeight){
19527             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19528         }
19529         
19530     },
19531     
19532     onTouchViewLoadException : function()
19533     {
19534         this.hideTouchView();
19535     },
19536     
19537     onTouchViewEmptyResults : function()
19538     {
19539         this.clearTouchView();
19540         
19541         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19542         
19543         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19544         
19545     },
19546     
19547     clearTouchView : function()
19548     {
19549         this.touchViewListGroup.dom.innerHTML = '';
19550     },
19551     
19552     onTouchViewClick : function(e, el, o)
19553     {
19554         e.preventDefault();
19555         
19556         var row = o.row;
19557         var rowIndex = o.rowIndex;
19558         
19559         var r = this.store.getAt(rowIndex);
19560         
19561         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19562             
19563             if(!this.multiple){
19564                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19565                     c.dom.removeAttribute('checked');
19566                 }, this);
19567
19568                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19569
19570                 this.setFromData(r.data);
19571
19572                 var close = this.closeTriggerEl();
19573
19574                 if(close){
19575                     close.show();
19576                 }
19577
19578                 this.hideTouchView();
19579
19580                 this.fireEvent('select', this, r, rowIndex);
19581
19582                 return;
19583             }
19584
19585             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19586                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19587                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19588                 return;
19589             }
19590
19591             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19592             this.addItem(r.data);
19593             this.tickItems.push(r.data);
19594         }
19595     },
19596     
19597     getAutoCreateNativeIOS : function()
19598     {
19599         var cfg = {
19600             cls: 'form-group' //input-group,
19601         };
19602         
19603         var combobox =  {
19604             tag: 'select',
19605             cls : 'roo-ios-select'
19606         };
19607         
19608         if (this.name) {
19609             combobox.name = this.name;
19610         }
19611         
19612         if (this.disabled) {
19613             combobox.disabled = true;
19614         }
19615         
19616         var settings = this;
19617         
19618         ['xs','sm','md','lg'].map(function(size){
19619             if (settings[size]) {
19620                 cfg.cls += ' col-' + size + '-' + settings[size];
19621             }
19622         });
19623         
19624         cfg.cn = combobox;
19625         
19626         return cfg;
19627         
19628     },
19629     
19630     initIOSView : function()
19631     {
19632         this.store.on('load', this.onIOSViewLoad, this);
19633         
19634         return;
19635     },
19636     
19637     onIOSViewLoad : function()
19638     {
19639         if(this.store.getCount() < 1){
19640             return;
19641         }
19642         
19643         this.clearIOSView();
19644         
19645         if(this.allowBlank) {
19646             
19647             var default_text = '-- SELECT --';
19648             
19649             if(this.placeholder.length){
19650                 default_text = this.placeholder;
19651             }
19652             
19653             if(this.emptyTitle.length){
19654                 default_text += ' - ' + this.emptyTitle + ' -';
19655             }
19656             
19657             var opt = this.inputEl().createChild({
19658                 tag: 'option',
19659                 value : 0,
19660                 html : default_text
19661             });
19662             
19663             var o = {};
19664             o[this.valueField] = 0;
19665             o[this.displayField] = default_text;
19666             
19667             this.ios_options.push({
19668                 data : o,
19669                 el : opt
19670             });
19671             
19672         }
19673         
19674         this.store.data.each(function(d, rowIndex){
19675             
19676             var html = '';
19677             
19678             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19679                 html = d.data[this.displayField];
19680             }
19681             
19682             var value = '';
19683             
19684             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19685                 value = d.data[this.valueField];
19686             }
19687             
19688             var option = {
19689                 tag: 'option',
19690                 value : value,
19691                 html : html
19692             };
19693             
19694             if(this.value == d.data[this.valueField]){
19695                 option['selected'] = true;
19696             }
19697             
19698             var opt = this.inputEl().createChild(option);
19699             
19700             this.ios_options.push({
19701                 data : d.data,
19702                 el : opt
19703             });
19704             
19705         }, this);
19706         
19707         this.inputEl().on('change', function(){
19708            this.fireEvent('select', this);
19709         }, this);
19710         
19711     },
19712     
19713     clearIOSView: function()
19714     {
19715         this.inputEl().dom.innerHTML = '';
19716         
19717         this.ios_options = [];
19718     },
19719     
19720     setIOSValue: function(v)
19721     {
19722         this.value = v;
19723         
19724         if(!this.ios_options){
19725             return;
19726         }
19727         
19728         Roo.each(this.ios_options, function(opts){
19729            
19730            opts.el.dom.removeAttribute('selected');
19731            
19732            if(opts.data[this.valueField] != v){
19733                return;
19734            }
19735            
19736            opts.el.dom.setAttribute('selected', true);
19737            
19738         }, this);
19739     }
19740
19741     /** 
19742     * @cfg {Boolean} grow 
19743     * @hide 
19744     */
19745     /** 
19746     * @cfg {Number} growMin 
19747     * @hide 
19748     */
19749     /** 
19750     * @cfg {Number} growMax 
19751     * @hide 
19752     */
19753     /**
19754      * @hide
19755      * @method autoSize
19756      */
19757 });
19758
19759 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19760     
19761     header : {
19762         tag: 'div',
19763         cls: 'modal-header',
19764         cn: [
19765             {
19766                 tag: 'h4',
19767                 cls: 'modal-title'
19768             }
19769         ]
19770     },
19771     
19772     body : {
19773         tag: 'div',
19774         cls: 'modal-body',
19775         cn: [
19776             {
19777                 tag: 'ul',
19778                 cls: 'list-group'
19779             }
19780         ]
19781     },
19782     
19783     listItemRadio : {
19784         tag: 'li',
19785         cls: 'list-group-item',
19786         cn: [
19787             {
19788                 tag: 'span',
19789                 cls: 'roo-combobox-list-group-item-value'
19790             },
19791             {
19792                 tag: 'div',
19793                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19794                 cn: [
19795                     {
19796                         tag: 'input',
19797                         type: 'radio'
19798                     },
19799                     {
19800                         tag: 'label'
19801                     }
19802                 ]
19803             }
19804         ]
19805     },
19806     
19807     listItemCheckbox : {
19808         tag: 'li',
19809         cls: 'list-group-item',
19810         cn: [
19811             {
19812                 tag: 'span',
19813                 cls: 'roo-combobox-list-group-item-value'
19814             },
19815             {
19816                 tag: 'div',
19817                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19818                 cn: [
19819                     {
19820                         tag: 'input',
19821                         type: 'checkbox'
19822                     },
19823                     {
19824                         tag: 'label'
19825                     }
19826                 ]
19827             }
19828         ]
19829     },
19830     
19831     emptyResult : {
19832         tag: 'div',
19833         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19834     },
19835     
19836     footer : {
19837         tag: 'div',
19838         cls: 'modal-footer',
19839         cn: [
19840             {
19841                 tag: 'div',
19842                 cls: 'row',
19843                 cn: [
19844                     {
19845                         tag: 'div',
19846                         cls: 'col-xs-6 text-left',
19847                         cn: {
19848                             tag: 'button',
19849                             cls: 'btn btn-danger roo-touch-view-cancel',
19850                             html: 'Cancel'
19851                         }
19852                     },
19853                     {
19854                         tag: 'div',
19855                         cls: 'col-xs-6 text-right',
19856                         cn: {
19857                             tag: 'button',
19858                             cls: 'btn btn-success roo-touch-view-ok',
19859                             html: 'OK'
19860                         }
19861                     }
19862                 ]
19863             }
19864         ]
19865         
19866     }
19867 });
19868
19869 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19870     
19871     touchViewTemplate : {
19872         tag: 'div',
19873         cls: 'modal fade roo-combobox-touch-view',
19874         cn: [
19875             {
19876                 tag: 'div',
19877                 cls: 'modal-dialog',
19878                 style : 'position:fixed', // we have to fix position....
19879                 cn: [
19880                     {
19881                         tag: 'div',
19882                         cls: 'modal-content',
19883                         cn: [
19884                             Roo.bootstrap.form.ComboBox.header,
19885                             Roo.bootstrap.form.ComboBox.body,
19886                             Roo.bootstrap.form.ComboBox.footer
19887                         ]
19888                     }
19889                 ]
19890             }
19891         ]
19892     }
19893 });/*
19894  * Based on:
19895  * Ext JS Library 1.1.1
19896  * Copyright(c) 2006-2007, Ext JS, LLC.
19897  *
19898  * Originally Released Under LGPL - original licence link has changed is not relivant.
19899  *
19900  * Fork - LGPL
19901  * <script type="text/javascript">
19902  */
19903
19904 /**
19905  * @class Roo.View
19906  * @extends Roo.util.Observable
19907  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19908  * This class also supports single and multi selection modes. <br>
19909  * Create a data model bound view:
19910  <pre><code>
19911  var store = new Roo.data.Store(...);
19912
19913  var view = new Roo.View({
19914     el : "my-element",
19915     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19916  
19917     singleSelect: true,
19918     selectedClass: "ydataview-selected",
19919     store: store
19920  });
19921
19922  // listen for node click?
19923  view.on("click", function(vw, index, node, e){
19924  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19925  });
19926
19927  // load XML data
19928  dataModel.load("foobar.xml");
19929  </code></pre>
19930  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19931  * <br><br>
19932  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19933  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19934  * 
19935  * Note: old style constructor is still suported (container, template, config)
19936  * 
19937  * @constructor
19938  * Create a new View
19939  * @param {Object} config The config object
19940  * 
19941  */
19942 Roo.View = function(config, depreciated_tpl, depreciated_config){
19943     
19944     this.parent = false;
19945     
19946     if (typeof(depreciated_tpl) == 'undefined') {
19947         // new way.. - universal constructor.
19948         Roo.apply(this, config);
19949         this.el  = Roo.get(this.el);
19950     } else {
19951         // old format..
19952         this.el  = Roo.get(config);
19953         this.tpl = depreciated_tpl;
19954         Roo.apply(this, depreciated_config);
19955     }
19956     this.wrapEl  = this.el.wrap().wrap();
19957     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19958     
19959     
19960     if(typeof(this.tpl) == "string"){
19961         this.tpl = new Roo.Template(this.tpl);
19962     } else {
19963         // support xtype ctors..
19964         this.tpl = new Roo.factory(this.tpl, Roo);
19965     }
19966     
19967     
19968     this.tpl.compile();
19969     
19970     /** @private */
19971     this.addEvents({
19972         /**
19973          * @event beforeclick
19974          * Fires before a click is processed. Returns false to cancel the default action.
19975          * @param {Roo.View} this
19976          * @param {Number} index The index of the target node
19977          * @param {HTMLElement} node The target node
19978          * @param {Roo.EventObject} e The raw event object
19979          */
19980             "beforeclick" : true,
19981         /**
19982          * @event click
19983          * Fires when a template node is clicked.
19984          * @param {Roo.View} this
19985          * @param {Number} index The index of the target node
19986          * @param {HTMLElement} node The target node
19987          * @param {Roo.EventObject} e The raw event object
19988          */
19989             "click" : true,
19990         /**
19991          * @event dblclick
19992          * Fires when a template node is double clicked.
19993          * @param {Roo.View} this
19994          * @param {Number} index The index of the target node
19995          * @param {HTMLElement} node The target node
19996          * @param {Roo.EventObject} e The raw event object
19997          */
19998             "dblclick" : true,
19999         /**
20000          * @event contextmenu
20001          * Fires when a template node is right clicked.
20002          * @param {Roo.View} this
20003          * @param {Number} index The index of the target node
20004          * @param {HTMLElement} node The target node
20005          * @param {Roo.EventObject} e The raw event object
20006          */
20007             "contextmenu" : true,
20008         /**
20009          * @event selectionchange
20010          * Fires when the selected nodes change.
20011          * @param {Roo.View} this
20012          * @param {Array} selections Array of the selected nodes
20013          */
20014             "selectionchange" : true,
20015     
20016         /**
20017          * @event beforeselect
20018          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20019          * @param {Roo.View} this
20020          * @param {HTMLElement} node The node to be selected
20021          * @param {Array} selections Array of currently selected nodes
20022          */
20023             "beforeselect" : true,
20024         /**
20025          * @event preparedata
20026          * Fires on every row to render, to allow you to change the data.
20027          * @param {Roo.View} this
20028          * @param {Object} data to be rendered (change this)
20029          */
20030           "preparedata" : true
20031           
20032           
20033         });
20034
20035
20036
20037     this.el.on({
20038         "click": this.onClick,
20039         "dblclick": this.onDblClick,
20040         "contextmenu": this.onContextMenu,
20041         scope:this
20042     });
20043
20044     this.selections = [];
20045     this.nodes = [];
20046     this.cmp = new Roo.CompositeElementLite([]);
20047     if(this.store){
20048         this.store = Roo.factory(this.store, Roo.data);
20049         this.setStore(this.store, true);
20050     }
20051     
20052     if ( this.footer && this.footer.xtype) {
20053            
20054          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20055         
20056         this.footer.dataSource = this.store;
20057         this.footer.container = fctr;
20058         this.footer = Roo.factory(this.footer, Roo);
20059         fctr.insertFirst(this.el);
20060         
20061         // this is a bit insane - as the paging toolbar seems to detach the el..
20062 //        dom.parentNode.parentNode.parentNode
20063          // they get detached?
20064     }
20065     
20066     
20067     Roo.View.superclass.constructor.call(this);
20068     
20069     
20070 };
20071
20072 Roo.extend(Roo.View, Roo.util.Observable, {
20073     
20074      /**
20075      * @cfg {Roo.data.Store} store Data store to load data from.
20076      */
20077     store : false,
20078     
20079     /**
20080      * @cfg {String|Roo.Element} el The container element.
20081      */
20082     el : '',
20083     
20084     /**
20085      * @cfg {String|Roo.Template} tpl The template used by this View 
20086      */
20087     tpl : false,
20088     /**
20089      * @cfg {String} dataName the named area of the template to use as the data area
20090      *                          Works with domtemplates roo-name="name"
20091      */
20092     dataName: false,
20093     /**
20094      * @cfg {String} selectedClass The css class to add to selected nodes
20095      */
20096     selectedClass : "x-view-selected",
20097      /**
20098      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20099      */
20100     emptyText : "",
20101     
20102     /**
20103      * @cfg {String} text to display on mask (default Loading)
20104      */
20105     mask : false,
20106     /**
20107      * @cfg {Boolean} multiSelect Allow multiple selection
20108      */
20109     multiSelect : false,
20110     /**
20111      * @cfg {Boolean} singleSelect Allow single selection
20112      */
20113     singleSelect:  false,
20114     
20115     /**
20116      * @cfg {Boolean} toggleSelect - selecting 
20117      */
20118     toggleSelect : false,
20119     
20120     /**
20121      * @cfg {Boolean} tickable - selecting 
20122      */
20123     tickable : false,
20124     
20125     /**
20126      * Returns the element this view is bound to.
20127      * @return {Roo.Element}
20128      */
20129     getEl : function(){
20130         return this.wrapEl;
20131     },
20132     
20133     
20134
20135     /**
20136      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20137      */
20138     refresh : function(){
20139         //Roo.log('refresh');
20140         var t = this.tpl;
20141         
20142         // if we are using something like 'domtemplate', then
20143         // the what gets used is:
20144         // t.applySubtemplate(NAME, data, wrapping data..)
20145         // the outer template then get' applied with
20146         //     the store 'extra data'
20147         // and the body get's added to the
20148         //      roo-name="data" node?
20149         //      <span class='roo-tpl-{name}'></span> ?????
20150         
20151         
20152         
20153         this.clearSelections();
20154         this.el.update("");
20155         var html = [];
20156         var records = this.store.getRange();
20157         if(records.length < 1) {
20158             
20159             // is this valid??  = should it render a template??
20160             
20161             this.el.update(this.emptyText);
20162             return;
20163         }
20164         var el = this.el;
20165         if (this.dataName) {
20166             this.el.update(t.apply(this.store.meta)); //????
20167             el = this.el.child('.roo-tpl-' + this.dataName);
20168         }
20169         
20170         for(var i = 0, len = records.length; i < len; i++){
20171             var data = this.prepareData(records[i].data, i, records[i]);
20172             this.fireEvent("preparedata", this, data, i, records[i]);
20173             
20174             var d = Roo.apply({}, data);
20175             
20176             if(this.tickable){
20177                 Roo.apply(d, {'roo-id' : Roo.id()});
20178                 
20179                 var _this = this;
20180             
20181                 Roo.each(this.parent.item, function(item){
20182                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20183                         return;
20184                     }
20185                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20186                 });
20187             }
20188             
20189             html[html.length] = Roo.util.Format.trim(
20190                 this.dataName ?
20191                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20192                     t.apply(d)
20193             );
20194         }
20195         
20196         
20197         
20198         el.update(html.join(""));
20199         this.nodes = el.dom.childNodes;
20200         this.updateIndexes(0);
20201     },
20202     
20203
20204     /**
20205      * Function to override to reformat the data that is sent to
20206      * the template for each node.
20207      * DEPRICATED - use the preparedata event handler.
20208      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20209      * a JSON object for an UpdateManager bound view).
20210      */
20211     prepareData : function(data, index, record)
20212     {
20213         this.fireEvent("preparedata", this, data, index, record);
20214         return data;
20215     },
20216
20217     onUpdate : function(ds, record){
20218         // Roo.log('on update');   
20219         this.clearSelections();
20220         var index = this.store.indexOf(record);
20221         var n = this.nodes[index];
20222         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20223         n.parentNode.removeChild(n);
20224         this.updateIndexes(index, index);
20225     },
20226
20227     
20228     
20229 // --------- FIXME     
20230     onAdd : function(ds, records, index)
20231     {
20232         //Roo.log(['on Add', ds, records, index] );        
20233         this.clearSelections();
20234         if(this.nodes.length == 0){
20235             this.refresh();
20236             return;
20237         }
20238         var n = this.nodes[index];
20239         for(var i = 0, len = records.length; i < len; i++){
20240             var d = this.prepareData(records[i].data, i, records[i]);
20241             if(n){
20242                 this.tpl.insertBefore(n, d);
20243             }else{
20244                 
20245                 this.tpl.append(this.el, d);
20246             }
20247         }
20248         this.updateIndexes(index);
20249     },
20250
20251     onRemove : function(ds, record, index){
20252        // Roo.log('onRemove');
20253         this.clearSelections();
20254         var el = this.dataName  ?
20255             this.el.child('.roo-tpl-' + this.dataName) :
20256             this.el; 
20257         
20258         el.dom.removeChild(this.nodes[index]);
20259         this.updateIndexes(index);
20260     },
20261
20262     /**
20263      * Refresh an individual node.
20264      * @param {Number} index
20265      */
20266     refreshNode : function(index){
20267         this.onUpdate(this.store, this.store.getAt(index));
20268     },
20269
20270     updateIndexes : function(startIndex, endIndex){
20271         var ns = this.nodes;
20272         startIndex = startIndex || 0;
20273         endIndex = endIndex || ns.length - 1;
20274         for(var i = startIndex; i <= endIndex; i++){
20275             ns[i].nodeIndex = i;
20276         }
20277     },
20278
20279     /**
20280      * Changes the data store this view uses and refresh the view.
20281      * @param {Store} store
20282      */
20283     setStore : function(store, initial){
20284         if(!initial && this.store){
20285             this.store.un("datachanged", this.refresh);
20286             this.store.un("add", this.onAdd);
20287             this.store.un("remove", this.onRemove);
20288             this.store.un("update", this.onUpdate);
20289             this.store.un("clear", this.refresh);
20290             this.store.un("beforeload", this.onBeforeLoad);
20291             this.store.un("load", this.onLoad);
20292             this.store.un("loadexception", this.onLoad);
20293         }
20294         if(store){
20295           
20296             store.on("datachanged", this.refresh, this);
20297             store.on("add", this.onAdd, this);
20298             store.on("remove", this.onRemove, this);
20299             store.on("update", this.onUpdate, this);
20300             store.on("clear", this.refresh, this);
20301             store.on("beforeload", this.onBeforeLoad, this);
20302             store.on("load", this.onLoad, this);
20303             store.on("loadexception", this.onLoad, this);
20304         }
20305         
20306         if(store){
20307             this.refresh();
20308         }
20309     },
20310     /**
20311      * onbeforeLoad - masks the loading area.
20312      *
20313      */
20314     onBeforeLoad : function(store,opts)
20315     {
20316          //Roo.log('onBeforeLoad');   
20317         if (!opts.add) {
20318             this.el.update("");
20319         }
20320         this.el.mask(this.mask ? this.mask : "Loading" ); 
20321     },
20322     onLoad : function ()
20323     {
20324         this.el.unmask();
20325     },
20326     
20327
20328     /**
20329      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20330      * @param {HTMLElement} node
20331      * @return {HTMLElement} The template node
20332      */
20333     findItemFromChild : function(node){
20334         var el = this.dataName  ?
20335             this.el.child('.roo-tpl-' + this.dataName,true) :
20336             this.el.dom; 
20337         
20338         if(!node || node.parentNode == el){
20339                     return node;
20340             }
20341             var p = node.parentNode;
20342             while(p && p != el){
20343             if(p.parentNode == el){
20344                 return p;
20345             }
20346             p = p.parentNode;
20347         }
20348             return null;
20349     },
20350
20351     /** @ignore */
20352     onClick : function(e){
20353         var item = this.findItemFromChild(e.getTarget());
20354         if(item){
20355             var index = this.indexOf(item);
20356             if(this.onItemClick(item, index, e) !== false){
20357                 this.fireEvent("click", this, index, item, e);
20358             }
20359         }else{
20360             this.clearSelections();
20361         }
20362     },
20363
20364     /** @ignore */
20365     onContextMenu : function(e){
20366         var item = this.findItemFromChild(e.getTarget());
20367         if(item){
20368             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20369         }
20370     },
20371
20372     /** @ignore */
20373     onDblClick : function(e){
20374         var item = this.findItemFromChild(e.getTarget());
20375         if(item){
20376             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20377         }
20378     },
20379
20380     onItemClick : function(item, index, e)
20381     {
20382         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20383             return false;
20384         }
20385         if (this.toggleSelect) {
20386             var m = this.isSelected(item) ? 'unselect' : 'select';
20387             //Roo.log(m);
20388             var _t = this;
20389             _t[m](item, true, false);
20390             return true;
20391         }
20392         if(this.multiSelect || this.singleSelect){
20393             if(this.multiSelect && e.shiftKey && this.lastSelection){
20394                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20395             }else{
20396                 this.select(item, this.multiSelect && e.ctrlKey);
20397                 this.lastSelection = item;
20398             }
20399             
20400             if(!this.tickable){
20401                 e.preventDefault();
20402             }
20403             
20404         }
20405         return true;
20406     },
20407
20408     /**
20409      * Get the number of selected nodes.
20410      * @return {Number}
20411      */
20412     getSelectionCount : function(){
20413         return this.selections.length;
20414     },
20415
20416     /**
20417      * Get the currently selected nodes.
20418      * @return {Array} An array of HTMLElements
20419      */
20420     getSelectedNodes : function(){
20421         return this.selections;
20422     },
20423
20424     /**
20425      * Get the indexes of the selected nodes.
20426      * @return {Array}
20427      */
20428     getSelectedIndexes : function(){
20429         var indexes = [], s = this.selections;
20430         for(var i = 0, len = s.length; i < len; i++){
20431             indexes.push(s[i].nodeIndex);
20432         }
20433         return indexes;
20434     },
20435
20436     /**
20437      * Clear all selections
20438      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20439      */
20440     clearSelections : function(suppressEvent){
20441         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20442             this.cmp.elements = this.selections;
20443             this.cmp.removeClass(this.selectedClass);
20444             this.selections = [];
20445             if(!suppressEvent){
20446                 this.fireEvent("selectionchange", this, this.selections);
20447             }
20448         }
20449     },
20450
20451     /**
20452      * Returns true if the passed node is selected
20453      * @param {HTMLElement/Number} node The node or node index
20454      * @return {Boolean}
20455      */
20456     isSelected : function(node){
20457         var s = this.selections;
20458         if(s.length < 1){
20459             return false;
20460         }
20461         node = this.getNode(node);
20462         return s.indexOf(node) !== -1;
20463     },
20464
20465     /**
20466      * Selects nodes.
20467      * @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
20468      * @param {Boolean} keepExisting (optional) true to keep existing selections
20469      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20470      */
20471     select : function(nodeInfo, keepExisting, suppressEvent){
20472         if(nodeInfo instanceof Array){
20473             if(!keepExisting){
20474                 this.clearSelections(true);
20475             }
20476             for(var i = 0, len = nodeInfo.length; i < len; i++){
20477                 this.select(nodeInfo[i], true, true);
20478             }
20479             return;
20480         } 
20481         var node = this.getNode(nodeInfo);
20482         if(!node || this.isSelected(node)){
20483             return; // already selected.
20484         }
20485         if(!keepExisting){
20486             this.clearSelections(true);
20487         }
20488         
20489         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20490             Roo.fly(node).addClass(this.selectedClass);
20491             this.selections.push(node);
20492             if(!suppressEvent){
20493                 this.fireEvent("selectionchange", this, this.selections);
20494             }
20495         }
20496         
20497         
20498     },
20499       /**
20500      * Unselects nodes.
20501      * @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
20502      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20503      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20504      */
20505     unselect : function(nodeInfo, keepExisting, suppressEvent)
20506     {
20507         if(nodeInfo instanceof Array){
20508             Roo.each(this.selections, function(s) {
20509                 this.unselect(s, nodeInfo);
20510             }, this);
20511             return;
20512         }
20513         var node = this.getNode(nodeInfo);
20514         if(!node || !this.isSelected(node)){
20515             //Roo.log("not selected");
20516             return; // not selected.
20517         }
20518         // fireevent???
20519         var ns = [];
20520         Roo.each(this.selections, function(s) {
20521             if (s == node ) {
20522                 Roo.fly(node).removeClass(this.selectedClass);
20523
20524                 return;
20525             }
20526             ns.push(s);
20527         },this);
20528         
20529         this.selections= ns;
20530         this.fireEvent("selectionchange", this, this.selections);
20531     },
20532
20533     /**
20534      * Gets a template node.
20535      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20536      * @return {HTMLElement} The node or null if it wasn't found
20537      */
20538     getNode : function(nodeInfo){
20539         if(typeof nodeInfo == "string"){
20540             return document.getElementById(nodeInfo);
20541         }else if(typeof nodeInfo == "number"){
20542             return this.nodes[nodeInfo];
20543         }
20544         return nodeInfo;
20545     },
20546
20547     /**
20548      * Gets a range template nodes.
20549      * @param {Number} startIndex
20550      * @param {Number} endIndex
20551      * @return {Array} An array of nodes
20552      */
20553     getNodes : function(start, end){
20554         var ns = this.nodes;
20555         start = start || 0;
20556         end = typeof end == "undefined" ? ns.length - 1 : end;
20557         var nodes = [];
20558         if(start <= end){
20559             for(var i = start; i <= end; i++){
20560                 nodes.push(ns[i]);
20561             }
20562         } else{
20563             for(var i = start; i >= end; i--){
20564                 nodes.push(ns[i]);
20565             }
20566         }
20567         return nodes;
20568     },
20569
20570     /**
20571      * Finds the index of the passed node
20572      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20573      * @return {Number} The index of the node or -1
20574      */
20575     indexOf : function(node){
20576         node = this.getNode(node);
20577         if(typeof node.nodeIndex == "number"){
20578             return node.nodeIndex;
20579         }
20580         var ns = this.nodes;
20581         for(var i = 0, len = ns.length; i < len; i++){
20582             if(ns[i] == node){
20583                 return i;
20584             }
20585         }
20586         return -1;
20587     }
20588 });
20589 /*
20590  * - LGPL
20591  *
20592  * based on jquery fullcalendar
20593  * 
20594  */
20595
20596 Roo.bootstrap = Roo.bootstrap || {};
20597 /**
20598  * @class Roo.bootstrap.Calendar
20599  * @extends Roo.bootstrap.Component
20600  * Bootstrap Calendar class
20601  * @cfg {Boolean} loadMask (true|false) default false
20602  * @cfg {Object} header generate the user specific header of the calendar, default false
20603
20604  * @constructor
20605  * Create a new Container
20606  * @param {Object} config The config object
20607  */
20608
20609
20610
20611 Roo.bootstrap.Calendar = function(config){
20612     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20613      this.addEvents({
20614         /**
20615              * @event select
20616              * Fires when a date is selected
20617              * @param {DatePicker} this
20618              * @param {Date} date The selected date
20619              */
20620         'select': true,
20621         /**
20622              * @event monthchange
20623              * Fires when the displayed month changes 
20624              * @param {DatePicker} this
20625              * @param {Date} date The selected month
20626              */
20627         'monthchange': true,
20628         /**
20629              * @event evententer
20630              * Fires when mouse over an event
20631              * @param {Calendar} this
20632              * @param {event} Event
20633              */
20634         'evententer': true,
20635         /**
20636              * @event eventleave
20637              * Fires when the mouse leaves an
20638              * @param {Calendar} this
20639              * @param {event}
20640              */
20641         'eventleave': true,
20642         /**
20643              * @event eventclick
20644              * Fires when the mouse click an
20645              * @param {Calendar} this
20646              * @param {event}
20647              */
20648         'eventclick': true
20649         
20650     });
20651
20652 };
20653
20654 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20655     
20656           /**
20657      * @cfg {Roo.data.Store} store
20658      * The data source for the calendar
20659      */
20660         store : false,
20661      /**
20662      * @cfg {Number} startDay
20663      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20664      */
20665     startDay : 0,
20666     
20667     loadMask : false,
20668     
20669     header : false,
20670       
20671     getAutoCreate : function(){
20672         
20673         
20674         var fc_button = function(name, corner, style, content ) {
20675             return Roo.apply({},{
20676                 tag : 'span',
20677                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20678                          (corner.length ?
20679                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20680                             ''
20681                         ),
20682                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20683                 unselectable: 'on'
20684             });
20685         };
20686         
20687         var header = {};
20688         
20689         if(!this.header){
20690             header = {
20691                 tag : 'table',
20692                 cls : 'fc-header',
20693                 style : 'width:100%',
20694                 cn : [
20695                     {
20696                         tag: 'tr',
20697                         cn : [
20698                             {
20699                                 tag : 'td',
20700                                 cls : 'fc-header-left',
20701                                 cn : [
20702                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20703                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20704                                     { tag: 'span', cls: 'fc-header-space' },
20705                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20706
20707
20708                                 ]
20709                             },
20710
20711                             {
20712                                 tag : 'td',
20713                                 cls : 'fc-header-center',
20714                                 cn : [
20715                                     {
20716                                         tag: 'span',
20717                                         cls: 'fc-header-title',
20718                                         cn : {
20719                                             tag: 'H2',
20720                                             html : 'month / year'
20721                                         }
20722                                     }
20723
20724                                 ]
20725                             },
20726                             {
20727                                 tag : 'td',
20728                                 cls : 'fc-header-right',
20729                                 cn : [
20730                               /*      fc_button('month', 'left', '', 'month' ),
20731                                     fc_button('week', '', '', 'week' ),
20732                                     fc_button('day', 'right', '', 'day' )
20733                                 */    
20734
20735                                 ]
20736                             }
20737
20738                         ]
20739                     }
20740                 ]
20741             };
20742         }
20743         
20744         header = this.header;
20745         
20746        
20747         var cal_heads = function() {
20748             var ret = [];
20749             // fixme - handle this.
20750             
20751             for (var i =0; i < Date.dayNames.length; i++) {
20752                 var d = Date.dayNames[i];
20753                 ret.push({
20754                     tag: 'th',
20755                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20756                     html : d.substring(0,3)
20757                 });
20758                 
20759             }
20760             ret[0].cls += ' fc-first';
20761             ret[6].cls += ' fc-last';
20762             return ret;
20763         };
20764         var cal_cell = function(n) {
20765             return  {
20766                 tag: 'td',
20767                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20768                 cn : [
20769                     {
20770                         cn : [
20771                             {
20772                                 cls: 'fc-day-number',
20773                                 html: 'D'
20774                             },
20775                             {
20776                                 cls: 'fc-day-content',
20777                              
20778                                 cn : [
20779                                      {
20780                                         style: 'position: relative;' // height: 17px;
20781                                     }
20782                                 ]
20783                             }
20784                             
20785                             
20786                         ]
20787                     }
20788                 ]
20789                 
20790             }
20791         };
20792         var cal_rows = function() {
20793             
20794             var ret = [];
20795             for (var r = 0; r < 6; r++) {
20796                 var row= {
20797                     tag : 'tr',
20798                     cls : 'fc-week',
20799                     cn : []
20800                 };
20801                 
20802                 for (var i =0; i < Date.dayNames.length; i++) {
20803                     var d = Date.dayNames[i];
20804                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20805
20806                 }
20807                 row.cn[0].cls+=' fc-first';
20808                 row.cn[0].cn[0].style = 'min-height:90px';
20809                 row.cn[6].cls+=' fc-last';
20810                 ret.push(row);
20811                 
20812             }
20813             ret[0].cls += ' fc-first';
20814             ret[4].cls += ' fc-prev-last';
20815             ret[5].cls += ' fc-last';
20816             return ret;
20817             
20818         };
20819         
20820         var cal_table = {
20821             tag: 'table',
20822             cls: 'fc-border-separate',
20823             style : 'width:100%',
20824             cellspacing  : 0,
20825             cn : [
20826                 { 
20827                     tag: 'thead',
20828                     cn : [
20829                         { 
20830                             tag: 'tr',
20831                             cls : 'fc-first fc-last',
20832                             cn : cal_heads()
20833                         }
20834                     ]
20835                 },
20836                 { 
20837                     tag: 'tbody',
20838                     cn : cal_rows()
20839                 }
20840                   
20841             ]
20842         };
20843          
20844          var cfg = {
20845             cls : 'fc fc-ltr',
20846             cn : [
20847                 header,
20848                 {
20849                     cls : 'fc-content',
20850                     style : "position: relative;",
20851                     cn : [
20852                         {
20853                             cls : 'fc-view fc-view-month fc-grid',
20854                             style : 'position: relative',
20855                             unselectable : 'on',
20856                             cn : [
20857                                 {
20858                                     cls : 'fc-event-container',
20859                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20860                                 },
20861                                 cal_table
20862                             ]
20863                         }
20864                     ]
20865     
20866                 }
20867            ] 
20868             
20869         };
20870         
20871          
20872         
20873         return cfg;
20874     },
20875     
20876     
20877     initEvents : function()
20878     {
20879         if(!this.store){
20880             throw "can not find store for calendar";
20881         }
20882         
20883         var mark = {
20884             tag: "div",
20885             cls:"x-dlg-mask",
20886             style: "text-align:center",
20887             cn: [
20888                 {
20889                     tag: "div",
20890                     style: "background-color:white;width:50%;margin:250 auto",
20891                     cn: [
20892                         {
20893                             tag: "img",
20894                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20895                         },
20896                         {
20897                             tag: "span",
20898                             html: "Loading"
20899                         }
20900                         
20901                     ]
20902                 }
20903             ]
20904         };
20905         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20906         
20907         var size = this.el.select('.fc-content', true).first().getSize();
20908         this.maskEl.setSize(size.width, size.height);
20909         this.maskEl.enableDisplayMode("block");
20910         if(!this.loadMask){
20911             this.maskEl.hide();
20912         }
20913         
20914         this.store = Roo.factory(this.store, Roo.data);
20915         this.store.on('load', this.onLoad, this);
20916         this.store.on('beforeload', this.onBeforeLoad, this);
20917         
20918         this.resize();
20919         
20920         this.cells = this.el.select('.fc-day',true);
20921         //Roo.log(this.cells);
20922         this.textNodes = this.el.query('.fc-day-number');
20923         this.cells.addClassOnOver('fc-state-hover');
20924         
20925         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20926         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20927         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20928         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20929         
20930         this.on('monthchange', this.onMonthChange, this);
20931         
20932         this.update(new Date().clearTime());
20933     },
20934     
20935     resize : function() {
20936         var sz  = this.el.getSize();
20937         
20938         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20939         this.el.select('.fc-day-content div',true).setHeight(34);
20940     },
20941     
20942     
20943     // private
20944     showPrevMonth : function(e){
20945         this.update(this.activeDate.add("mo", -1));
20946     },
20947     showToday : function(e){
20948         this.update(new Date().clearTime());
20949     },
20950     // private
20951     showNextMonth : function(e){
20952         this.update(this.activeDate.add("mo", 1));
20953     },
20954
20955     // private
20956     showPrevYear : function(){
20957         this.update(this.activeDate.add("y", -1));
20958     },
20959
20960     // private
20961     showNextYear : function(){
20962         this.update(this.activeDate.add("y", 1));
20963     },
20964
20965     
20966    // private
20967     update : function(date)
20968     {
20969         var vd = this.activeDate;
20970         this.activeDate = date;
20971 //        if(vd && this.el){
20972 //            var t = date.getTime();
20973 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20974 //                Roo.log('using add remove');
20975 //                
20976 //                this.fireEvent('monthchange', this, date);
20977 //                
20978 //                this.cells.removeClass("fc-state-highlight");
20979 //                this.cells.each(function(c){
20980 //                   if(c.dateValue == t){
20981 //                       c.addClass("fc-state-highlight");
20982 //                       setTimeout(function(){
20983 //                            try{c.dom.firstChild.focus();}catch(e){}
20984 //                       }, 50);
20985 //                       return false;
20986 //                   }
20987 //                   return true;
20988 //                });
20989 //                return;
20990 //            }
20991 //        }
20992         
20993         var days = date.getDaysInMonth();
20994         
20995         var firstOfMonth = date.getFirstDateOfMonth();
20996         var startingPos = firstOfMonth.getDay()-this.startDay;
20997         
20998         if(startingPos < this.startDay){
20999             startingPos += 7;
21000         }
21001         
21002         var pm = date.add(Date.MONTH, -1);
21003         var prevStart = pm.getDaysInMonth()-startingPos;
21004 //        
21005         this.cells = this.el.select('.fc-day',true);
21006         this.textNodes = this.el.query('.fc-day-number');
21007         this.cells.addClassOnOver('fc-state-hover');
21008         
21009         var cells = this.cells.elements;
21010         var textEls = this.textNodes;
21011         
21012         Roo.each(cells, function(cell){
21013             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21014         });
21015         
21016         days += startingPos;
21017
21018         // convert everything to numbers so it's fast
21019         var day = 86400000;
21020         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21021         //Roo.log(d);
21022         //Roo.log(pm);
21023         //Roo.log(prevStart);
21024         
21025         var today = new Date().clearTime().getTime();
21026         var sel = date.clearTime().getTime();
21027         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21028         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21029         var ddMatch = this.disabledDatesRE;
21030         var ddText = this.disabledDatesText;
21031         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21032         var ddaysText = this.disabledDaysText;
21033         var format = this.format;
21034         
21035         var setCellClass = function(cal, cell){
21036             cell.row = 0;
21037             cell.events = [];
21038             cell.more = [];
21039             //Roo.log('set Cell Class');
21040             cell.title = "";
21041             var t = d.getTime();
21042             
21043             //Roo.log(d);
21044             
21045             cell.dateValue = t;
21046             if(t == today){
21047                 cell.className += " fc-today";
21048                 cell.className += " fc-state-highlight";
21049                 cell.title = cal.todayText;
21050             }
21051             if(t == sel){
21052                 // disable highlight in other month..
21053                 //cell.className += " fc-state-highlight";
21054                 
21055             }
21056             // disabling
21057             if(t < min) {
21058                 cell.className = " fc-state-disabled";
21059                 cell.title = cal.minText;
21060                 return;
21061             }
21062             if(t > max) {
21063                 cell.className = " fc-state-disabled";
21064                 cell.title = cal.maxText;
21065                 return;
21066             }
21067             if(ddays){
21068                 if(ddays.indexOf(d.getDay()) != -1){
21069                     cell.title = ddaysText;
21070                     cell.className = " fc-state-disabled";
21071                 }
21072             }
21073             if(ddMatch && format){
21074                 var fvalue = d.dateFormat(format);
21075                 if(ddMatch.test(fvalue)){
21076                     cell.title = ddText.replace("%0", fvalue);
21077                     cell.className = " fc-state-disabled";
21078                 }
21079             }
21080             
21081             if (!cell.initialClassName) {
21082                 cell.initialClassName = cell.dom.className;
21083             }
21084             
21085             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21086         };
21087
21088         var i = 0;
21089         
21090         for(; i < startingPos; i++) {
21091             textEls[i].innerHTML = (++prevStart);
21092             d.setDate(d.getDate()+1);
21093             
21094             cells[i].className = "fc-past fc-other-month";
21095             setCellClass(this, cells[i]);
21096         }
21097         
21098         var intDay = 0;
21099         
21100         for(; i < days; i++){
21101             intDay = i - startingPos + 1;
21102             textEls[i].innerHTML = (intDay);
21103             d.setDate(d.getDate()+1);
21104             
21105             cells[i].className = ''; // "x-date-active";
21106             setCellClass(this, cells[i]);
21107         }
21108         var extraDays = 0;
21109         
21110         for(; i < 42; i++) {
21111             textEls[i].innerHTML = (++extraDays);
21112             d.setDate(d.getDate()+1);
21113             
21114             cells[i].className = "fc-future fc-other-month";
21115             setCellClass(this, cells[i]);
21116         }
21117         
21118         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21119         
21120         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21121         
21122         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21123         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21124         
21125         if(totalRows != 6){
21126             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21127             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21128         }
21129         
21130         this.fireEvent('monthchange', this, date);
21131         
21132         
21133         /*
21134         if(!this.internalRender){
21135             var main = this.el.dom.firstChild;
21136             var w = main.offsetWidth;
21137             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21138             Roo.fly(main).setWidth(w);
21139             this.internalRender = true;
21140             // opera does not respect the auto grow header center column
21141             // then, after it gets a width opera refuses to recalculate
21142             // without a second pass
21143             if(Roo.isOpera && !this.secondPass){
21144                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21145                 this.secondPass = true;
21146                 this.update.defer(10, this, [date]);
21147             }
21148         }
21149         */
21150         
21151     },
21152     
21153     findCell : function(dt) {
21154         dt = dt.clearTime().getTime();
21155         var ret = false;
21156         this.cells.each(function(c){
21157             //Roo.log("check " +c.dateValue + '?=' + dt);
21158             if(c.dateValue == dt){
21159                 ret = c;
21160                 return false;
21161             }
21162             return true;
21163         });
21164         
21165         return ret;
21166     },
21167     
21168     findCells : function(ev) {
21169         var s = ev.start.clone().clearTime().getTime();
21170        // Roo.log(s);
21171         var e= ev.end.clone().clearTime().getTime();
21172        // Roo.log(e);
21173         var ret = [];
21174         this.cells.each(function(c){
21175              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21176             
21177             if(c.dateValue > e){
21178                 return ;
21179             }
21180             if(c.dateValue < s){
21181                 return ;
21182             }
21183             ret.push(c);
21184         });
21185         
21186         return ret;    
21187     },
21188     
21189 //    findBestRow: function(cells)
21190 //    {
21191 //        var ret = 0;
21192 //        
21193 //        for (var i =0 ; i < cells.length;i++) {
21194 //            ret  = Math.max(cells[i].rows || 0,ret);
21195 //        }
21196 //        return ret;
21197 //        
21198 //    },
21199     
21200     
21201     addItem : function(ev)
21202     {
21203         // look for vertical location slot in
21204         var cells = this.findCells(ev);
21205         
21206 //        ev.row = this.findBestRow(cells);
21207         
21208         // work out the location.
21209         
21210         var crow = false;
21211         var rows = [];
21212         for(var i =0; i < cells.length; i++) {
21213             
21214             cells[i].row = cells[0].row;
21215             
21216             if(i == 0){
21217                 cells[i].row = cells[i].row + 1;
21218             }
21219             
21220             if (!crow) {
21221                 crow = {
21222                     start : cells[i],
21223                     end :  cells[i]
21224                 };
21225                 continue;
21226             }
21227             if (crow.start.getY() == cells[i].getY()) {
21228                 // on same row.
21229                 crow.end = cells[i];
21230                 continue;
21231             }
21232             // different row.
21233             rows.push(crow);
21234             crow = {
21235                 start: cells[i],
21236                 end : cells[i]
21237             };
21238             
21239         }
21240         
21241         rows.push(crow);
21242         ev.els = [];
21243         ev.rows = rows;
21244         ev.cells = cells;
21245         
21246         cells[0].events.push(ev);
21247         
21248         this.calevents.push(ev);
21249     },
21250     
21251     clearEvents: function() {
21252         
21253         if(!this.calevents){
21254             return;
21255         }
21256         
21257         Roo.each(this.cells.elements, function(c){
21258             c.row = 0;
21259             c.events = [];
21260             c.more = [];
21261         });
21262         
21263         Roo.each(this.calevents, function(e) {
21264             Roo.each(e.els, function(el) {
21265                 el.un('mouseenter' ,this.onEventEnter, this);
21266                 el.un('mouseleave' ,this.onEventLeave, this);
21267                 el.remove();
21268             },this);
21269         },this);
21270         
21271         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21272             e.remove();
21273         });
21274         
21275     },
21276     
21277     renderEvents: function()
21278     {   
21279         var _this = this;
21280         
21281         this.cells.each(function(c) {
21282             
21283             if(c.row < 5){
21284                 return;
21285             }
21286             
21287             var ev = c.events;
21288             
21289             var r = 4;
21290             if(c.row != c.events.length){
21291                 r = 4 - (4 - (c.row - c.events.length));
21292             }
21293             
21294             c.events = ev.slice(0, r);
21295             c.more = ev.slice(r);
21296             
21297             if(c.more.length && c.more.length == 1){
21298                 c.events.push(c.more.pop());
21299             }
21300             
21301             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21302             
21303         });
21304             
21305         this.cells.each(function(c) {
21306             
21307             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21308             
21309             
21310             for (var e = 0; e < c.events.length; e++){
21311                 var ev = c.events[e];
21312                 var rows = ev.rows;
21313                 
21314                 for(var i = 0; i < rows.length; i++) {
21315                 
21316                     // how many rows should it span..
21317
21318                     var  cfg = {
21319                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21320                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21321
21322                         unselectable : "on",
21323                         cn : [
21324                             {
21325                                 cls: 'fc-event-inner',
21326                                 cn : [
21327     //                                {
21328     //                                  tag:'span',
21329     //                                  cls: 'fc-event-time',
21330     //                                  html : cells.length > 1 ? '' : ev.time
21331     //                                },
21332                                     {
21333                                       tag:'span',
21334                                       cls: 'fc-event-title',
21335                                       html : String.format('{0}', ev.title)
21336                                     }
21337
21338
21339                                 ]
21340                             },
21341                             {
21342                                 cls: 'ui-resizable-handle ui-resizable-e',
21343                                 html : '&nbsp;&nbsp;&nbsp'
21344                             }
21345
21346                         ]
21347                     };
21348
21349                     if (i == 0) {
21350                         cfg.cls += ' fc-event-start';
21351                     }
21352                     if ((i+1) == rows.length) {
21353                         cfg.cls += ' fc-event-end';
21354                     }
21355
21356                     var ctr = _this.el.select('.fc-event-container',true).first();
21357                     var cg = ctr.createChild(cfg);
21358
21359                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21360                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21361
21362                     var r = (c.more.length) ? 1 : 0;
21363                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21364                     cg.setWidth(ebox.right - sbox.x -2);
21365
21366                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21367                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21368                     cg.on('click', _this.onEventClick, _this, ev);
21369
21370                     ev.els.push(cg);
21371                     
21372                 }
21373                 
21374             }
21375             
21376             
21377             if(c.more.length){
21378                 var  cfg = {
21379                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21380                     style : 'position: absolute',
21381                     unselectable : "on",
21382                     cn : [
21383                         {
21384                             cls: 'fc-event-inner',
21385                             cn : [
21386                                 {
21387                                   tag:'span',
21388                                   cls: 'fc-event-title',
21389                                   html : 'More'
21390                                 }
21391
21392
21393                             ]
21394                         },
21395                         {
21396                             cls: 'ui-resizable-handle ui-resizable-e',
21397                             html : '&nbsp;&nbsp;&nbsp'
21398                         }
21399
21400                     ]
21401                 };
21402
21403                 var ctr = _this.el.select('.fc-event-container',true).first();
21404                 var cg = ctr.createChild(cfg);
21405
21406                 var sbox = c.select('.fc-day-content',true).first().getBox();
21407                 var ebox = c.select('.fc-day-content',true).first().getBox();
21408                 //Roo.log(cg);
21409                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21410                 cg.setWidth(ebox.right - sbox.x -2);
21411
21412                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21413                 
21414             }
21415             
21416         });
21417         
21418         
21419         
21420     },
21421     
21422     onEventEnter: function (e, el,event,d) {
21423         this.fireEvent('evententer', this, el, event);
21424     },
21425     
21426     onEventLeave: function (e, el,event,d) {
21427         this.fireEvent('eventleave', this, el, event);
21428     },
21429     
21430     onEventClick: function (e, el,event,d) {
21431         this.fireEvent('eventclick', this, el, event);
21432     },
21433     
21434     onMonthChange: function () {
21435         this.store.load();
21436     },
21437     
21438     onMoreEventClick: function(e, el, more)
21439     {
21440         var _this = this;
21441         
21442         this.calpopover.placement = 'right';
21443         this.calpopover.setTitle('More');
21444         
21445         this.calpopover.setContent('');
21446         
21447         var ctr = this.calpopover.el.select('.popover-content', true).first();
21448         
21449         Roo.each(more, function(m){
21450             var cfg = {
21451                 cls : 'fc-event-hori fc-event-draggable',
21452                 html : m.title
21453             };
21454             var cg = ctr.createChild(cfg);
21455             
21456             cg.on('click', _this.onEventClick, _this, m);
21457         });
21458         
21459         this.calpopover.show(el);
21460         
21461         
21462     },
21463     
21464     onLoad: function () 
21465     {   
21466         this.calevents = [];
21467         var cal = this;
21468         
21469         if(this.store.getCount() > 0){
21470             this.store.data.each(function(d){
21471                cal.addItem({
21472                     id : d.data.id,
21473                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21474                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21475                     time : d.data.start_time,
21476                     title : d.data.title,
21477                     description : d.data.description,
21478                     venue : d.data.venue
21479                 });
21480             });
21481         }
21482         
21483         this.renderEvents();
21484         
21485         if(this.calevents.length && this.loadMask){
21486             this.maskEl.hide();
21487         }
21488     },
21489     
21490     onBeforeLoad: function()
21491     {
21492         this.clearEvents();
21493         if(this.loadMask){
21494             this.maskEl.show();
21495         }
21496     }
21497 });
21498
21499  
21500  /*
21501  * - LGPL
21502  *
21503  * element
21504  * 
21505  */
21506
21507 /**
21508  * @class Roo.bootstrap.Popover
21509  * @extends Roo.bootstrap.Component
21510  * @parent none builder
21511  * @children Roo.bootstrap.Component
21512  * Bootstrap Popover class
21513  * @cfg {String} html contents of the popover   (or false to use children..)
21514  * @cfg {String} title of popover (or false to hide)
21515  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21516  * @cfg {String} trigger click || hover (or false to trigger manually)
21517  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21518  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21519  *      - if false and it has a 'parent' then it will be automatically added to that element
21520  *      - if string - Roo.get  will be called 
21521  * @cfg {Number} delay - delay before showing
21522  
21523  * @constructor
21524  * Create a new Popover
21525  * @param {Object} config The config object
21526  */
21527
21528 Roo.bootstrap.Popover = function(config){
21529     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21530     
21531     this.addEvents({
21532         // raw events
21533          /**
21534          * @event show
21535          * After the popover show
21536          * 
21537          * @param {Roo.bootstrap.Popover} this
21538          */
21539         "show" : true,
21540         /**
21541          * @event hide
21542          * After the popover hide
21543          * 
21544          * @param {Roo.bootstrap.Popover} this
21545          */
21546         "hide" : true
21547     });
21548 };
21549
21550 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21551     
21552     title: false,
21553     html: false,
21554     
21555     placement : 'right',
21556     trigger : 'hover', // hover
21557     modal : false,
21558     delay : 0,
21559     
21560     over: false,
21561     
21562     can_build_overlaid : false,
21563     
21564     maskEl : false, // the mask element
21565     headerEl : false,
21566     contentEl : false,
21567     alignEl : false, // when show is called with an element - this get's stored.
21568     
21569     getChildContainer : function()
21570     {
21571         return this.contentEl;
21572         
21573     },
21574     getPopoverHeader : function()
21575     {
21576         this.title = true; // flag not to hide it..
21577         this.headerEl.addClass('p-0');
21578         return this.headerEl
21579     },
21580     
21581     
21582     getAutoCreate : function(){
21583          
21584         var cfg = {
21585            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21586            style: 'display:block',
21587            cn : [
21588                 {
21589                     cls : 'arrow'
21590                 },
21591                 {
21592                     cls : 'popover-inner ',
21593                     cn : [
21594                         {
21595                             tag: 'h3',
21596                             cls: 'popover-title popover-header',
21597                             html : this.title === false ? '' : this.title
21598                         },
21599                         {
21600                             cls : 'popover-content popover-body '  + (this.cls || ''),
21601                             html : this.html || ''
21602                         }
21603                     ]
21604                     
21605                 }
21606            ]
21607         };
21608         
21609         return cfg;
21610     },
21611     /**
21612      * @param {string} the title
21613      */
21614     setTitle: function(str)
21615     {
21616         this.title = str;
21617         if (this.el) {
21618             this.headerEl.dom.innerHTML = str;
21619         }
21620         
21621     },
21622     /**
21623      * @param {string} the body content
21624      */
21625     setContent: function(str)
21626     {
21627         this.html = str;
21628         if (this.contentEl) {
21629             this.contentEl.dom.innerHTML = str;
21630         }
21631         
21632     },
21633     // as it get's added to the bottom of the page.
21634     onRender : function(ct, position)
21635     {
21636         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21637         
21638         
21639         
21640         if(!this.el){
21641             var cfg = Roo.apply({},  this.getAutoCreate());
21642             cfg.id = Roo.id();
21643             
21644             if (this.cls) {
21645                 cfg.cls += ' ' + this.cls;
21646             }
21647             if (this.style) {
21648                 cfg.style = this.style;
21649             }
21650             //Roo.log("adding to ");
21651             this.el = Roo.get(document.body).createChild(cfg, position);
21652 //            Roo.log(this.el);
21653         }
21654         
21655         this.contentEl = this.el.select('.popover-content',true).first();
21656         this.headerEl =  this.el.select('.popover-title',true).first();
21657         
21658         var nitems = [];
21659         if(typeof(this.items) != 'undefined'){
21660             var items = this.items;
21661             delete this.items;
21662
21663             for(var i =0;i < items.length;i++) {
21664                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21665             }
21666         }
21667
21668         this.items = nitems;
21669         
21670         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21671         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21672         
21673         
21674         
21675         this.initEvents();
21676     },
21677     
21678     resizeMask : function()
21679     {
21680         this.maskEl.setSize(
21681             Roo.lib.Dom.getViewWidth(true),
21682             Roo.lib.Dom.getViewHeight(true)
21683         );
21684     },
21685     
21686     initEvents : function()
21687     {
21688         
21689         if (!this.modal) { 
21690             Roo.bootstrap.Popover.register(this);
21691         }
21692          
21693         this.arrowEl = this.el.select('.arrow',true).first();
21694         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21695         this.el.enableDisplayMode('block');
21696         this.el.hide();
21697  
21698         
21699         if (this.over === false && !this.parent()) {
21700             return; 
21701         }
21702         if (this.triggers === false) {
21703             return;
21704         }
21705          
21706         // support parent
21707         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21708         var triggers = this.trigger ? this.trigger.split(' ') : [];
21709         Roo.each(triggers, function(trigger) {
21710         
21711             if (trigger == 'click') {
21712                 on_el.on('click', this.toggle, this);
21713             } else if (trigger != 'manual') {
21714                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21715                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21716       
21717                 on_el.on(eventIn  ,this.enter, this);
21718                 on_el.on(eventOut, this.leave, this);
21719             }
21720         }, this);
21721     },
21722     
21723     
21724     // private
21725     timeout : null,
21726     hoverState : null,
21727     
21728     toggle : function () {
21729         this.hoverState == 'in' ? this.leave() : this.enter();
21730     },
21731     
21732     enter : function () {
21733         
21734         clearTimeout(this.timeout);
21735     
21736         this.hoverState = 'in';
21737     
21738         if (!this.delay || !this.delay.show) {
21739             this.show();
21740             return;
21741         }
21742         var _t = this;
21743         this.timeout = setTimeout(function () {
21744             if (_t.hoverState == 'in') {
21745                 _t.show();
21746             }
21747         }, this.delay.show)
21748     },
21749     
21750     leave : function() {
21751         clearTimeout(this.timeout);
21752     
21753         this.hoverState = 'out';
21754     
21755         if (!this.delay || !this.delay.hide) {
21756             this.hide();
21757             return;
21758         }
21759         var _t = this;
21760         this.timeout = setTimeout(function () {
21761             if (_t.hoverState == 'out') {
21762                 _t.hide();
21763             }
21764         }, this.delay.hide)
21765     },
21766     
21767     /**
21768      * update the position of the dialog
21769      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21770      * 
21771      *
21772      */
21773     
21774     doAlign : function()
21775     {
21776         
21777         if (this.alignEl) {
21778             this.updatePosition(this.placement, true);
21779              
21780         } else {
21781             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21782             var es = this.el.getSize();
21783             var x = Roo.lib.Dom.getViewWidth()/2;
21784             var y = Roo.lib.Dom.getViewHeight()/2;
21785             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21786             
21787         }
21788
21789          
21790          
21791         
21792         
21793     },
21794     
21795     /**
21796      * Show the popover
21797      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21798      * @param {string} (left|right|top|bottom) position
21799      */
21800     show : function (on_el, placement)
21801     {
21802         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21803         on_el = on_el || false; // default to false
21804          
21805         if (!on_el) {
21806             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21807                 on_el = this.parent().el;
21808             } else if (this.over) {
21809                 on_el = Roo.get(this.over);
21810             }
21811             
21812         }
21813         
21814         this.alignEl = Roo.get( on_el );
21815
21816         if (!this.el) {
21817             this.render(document.body);
21818         }
21819         
21820         
21821          
21822         
21823         if (this.title === false) {
21824             this.headerEl.hide();
21825         }
21826         
21827        
21828         this.el.show();
21829         this.el.dom.style.display = 'block';
21830          
21831         this.doAlign();
21832         
21833         //var arrow = this.el.select('.arrow',true).first();
21834         //arrow.set(align[2], 
21835         
21836         this.el.addClass('in');
21837         
21838          
21839         
21840         this.hoverState = 'in';
21841         
21842         if (this.modal) {
21843             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21844             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21845             this.maskEl.dom.style.display = 'block';
21846             this.maskEl.addClass('show');
21847         }
21848         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21849  
21850         this.fireEvent('show', this);
21851         
21852     },
21853     /**
21854      * fire this manually after loading a grid in the table for example
21855      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21856      * @param {Boolean} try and move it if we cant get right position.
21857      */
21858     updatePosition : function(placement, try_move)
21859     {
21860         // allow for calling with no parameters
21861         placement = placement   ? placement :  this.placement;
21862         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21863         
21864         this.el.removeClass([
21865             'fade','top','bottom', 'left', 'right','in',
21866             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21867         ]);
21868         this.el.addClass(placement + ' bs-popover-' + placement);
21869         
21870         if (!this.alignEl ) {
21871             return false;
21872         }
21873         
21874         switch (placement) {
21875             case 'right':
21876                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21877                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21878                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21879                     //normal display... or moved up/down.
21880                     this.el.setXY(offset);
21881                     var xy = this.alignEl.getAnchorXY('tr', false);
21882                     xy[0]+=2;xy[1]+=5;
21883                     this.arrowEl.setXY(xy);
21884                     return true;
21885                 }
21886                 // continue through...
21887                 return this.updatePosition('left', false);
21888                 
21889             
21890             case 'left':
21891                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21892                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21893                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21894                     //normal display... or moved up/down.
21895                     this.el.setXY(offset);
21896                     var xy = this.alignEl.getAnchorXY('tl', false);
21897                     xy[0]-=10;xy[1]+=5; // << fix me
21898                     this.arrowEl.setXY(xy);
21899                     return true;
21900                 }
21901                 // call self...
21902                 return this.updatePosition('right', false);
21903             
21904             case 'top':
21905                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21906                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21907                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21908                     //normal display... or moved up/down.
21909                     this.el.setXY(offset);
21910                     var xy = this.alignEl.getAnchorXY('t', false);
21911                     xy[1]-=10; // << fix me
21912                     this.arrowEl.setXY(xy);
21913                     return true;
21914                 }
21915                 // fall through
21916                return this.updatePosition('bottom', false);
21917             
21918             case 'bottom':
21919                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21920                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21921                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21922                     //normal display... or moved up/down.
21923                     this.el.setXY(offset);
21924                     var xy = this.alignEl.getAnchorXY('b', false);
21925                      xy[1]+=2; // << fix me
21926                     this.arrowEl.setXY(xy);
21927                     return true;
21928                 }
21929                 // fall through
21930                 return this.updatePosition('top', false);
21931                 
21932             
21933         }
21934         
21935         
21936         return false;
21937     },
21938     
21939     hide : function()
21940     {
21941         this.el.setXY([0,0]);
21942         this.el.removeClass('in');
21943         this.el.hide();
21944         this.hoverState = null;
21945         this.maskEl.hide(); // always..
21946         this.fireEvent('hide', this);
21947     }
21948     
21949 });
21950
21951
21952 Roo.apply(Roo.bootstrap.Popover, {
21953
21954     alignment : {
21955         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21956         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21957         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21958         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21959     },
21960     
21961     zIndex : 20001,
21962
21963     clickHander : false,
21964     
21965     
21966
21967     onMouseDown : function(e)
21968     {
21969         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21970             /// what is nothing is showing..
21971             this.hideAll();
21972         }
21973          
21974     },
21975     
21976     
21977     popups : [],
21978     
21979     register : function(popup)
21980     {
21981         if (!Roo.bootstrap.Popover.clickHandler) {
21982             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21983         }
21984         // hide other popups.
21985         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21986         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21987         this.hideAll(); //<< why?
21988         //this.popups.push(popup);
21989     },
21990     hideAll : function()
21991     {
21992         this.popups.forEach(function(p) {
21993             p.hide();
21994         });
21995     },
21996     onShow : function() {
21997         Roo.bootstrap.Popover.popups.push(this);
21998     },
21999     onHide : function() {
22000         Roo.bootstrap.Popover.popups.remove(this);
22001     } 
22002
22003 });
22004 /**
22005  * @class Roo.bootstrap.PopoverNav
22006  * @extends Roo.bootstrap.nav.Simplebar
22007  * @parent Roo.bootstrap.Popover
22008  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22009  * @licence LGPL
22010  * Bootstrap Popover header navigation class
22011  * FIXME? should this go under nav?
22012  *
22013  * 
22014  * @constructor
22015  * Create a new Popover Header Navigation 
22016  * @param {Object} config The config object
22017  */
22018
22019 Roo.bootstrap.PopoverNav = function(config){
22020     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22021 };
22022
22023 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22024     
22025     
22026     container_method : 'getPopoverHeader' 
22027     
22028      
22029     
22030     
22031    
22032 });
22033
22034  
22035
22036  /*
22037  * - LGPL
22038  *
22039  * Progress
22040  * 
22041  */
22042
22043 /**
22044  * @class Roo.bootstrap.Progress
22045  * @extends Roo.bootstrap.Component
22046  * @children Roo.bootstrap.ProgressBar
22047  * Bootstrap Progress class
22048  * @cfg {Boolean} striped striped of the progress bar
22049  * @cfg {Boolean} active animated of the progress bar
22050  * 
22051  * 
22052  * @constructor
22053  * Create a new Progress
22054  * @param {Object} config The config object
22055  */
22056
22057 Roo.bootstrap.Progress = function(config){
22058     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22059 };
22060
22061 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22062     
22063     striped : false,
22064     active: false,
22065     
22066     getAutoCreate : function(){
22067         var cfg = {
22068             tag: 'div',
22069             cls: 'progress'
22070         };
22071         
22072         
22073         if(this.striped){
22074             cfg.cls += ' progress-striped';
22075         }
22076       
22077         if(this.active){
22078             cfg.cls += ' active';
22079         }
22080         
22081         
22082         return cfg;
22083     }
22084    
22085 });
22086
22087  
22088
22089  /*
22090  * - LGPL
22091  *
22092  * ProgressBar
22093  * 
22094  */
22095
22096 /**
22097  * @class Roo.bootstrap.ProgressBar
22098  * @extends Roo.bootstrap.Component
22099  * Bootstrap ProgressBar class
22100  * @cfg {Number} aria_valuenow aria-value now
22101  * @cfg {Number} aria_valuemin aria-value min
22102  * @cfg {Number} aria_valuemax aria-value max
22103  * @cfg {String} label label for the progress bar
22104  * @cfg {String} panel (success | info | warning | danger )
22105  * @cfg {String} role role of the progress bar
22106  * @cfg {String} sr_only text
22107  * 
22108  * 
22109  * @constructor
22110  * Create a new ProgressBar
22111  * @param {Object} config The config object
22112  */
22113
22114 Roo.bootstrap.ProgressBar = function(config){
22115     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22116 };
22117
22118 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22119     
22120     aria_valuenow : 0,
22121     aria_valuemin : 0,
22122     aria_valuemax : 100,
22123     label : false,
22124     panel : false,
22125     role : false,
22126     sr_only: false,
22127     
22128     getAutoCreate : function()
22129     {
22130         
22131         var cfg = {
22132             tag: 'div',
22133             cls: 'progress-bar',
22134             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22135         };
22136         
22137         if(this.sr_only){
22138             cfg.cn = {
22139                 tag: 'span',
22140                 cls: 'sr-only',
22141                 html: this.sr_only
22142             }
22143         }
22144         
22145         if(this.role){
22146             cfg.role = this.role;
22147         }
22148         
22149         if(this.aria_valuenow){
22150             cfg['aria-valuenow'] = this.aria_valuenow;
22151         }
22152         
22153         if(this.aria_valuemin){
22154             cfg['aria-valuemin'] = this.aria_valuemin;
22155         }
22156         
22157         if(this.aria_valuemax){
22158             cfg['aria-valuemax'] = this.aria_valuemax;
22159         }
22160         
22161         if(this.label && !this.sr_only){
22162             cfg.html = this.label;
22163         }
22164         
22165         if(this.panel){
22166             cfg.cls += ' progress-bar-' + this.panel;
22167         }
22168         
22169         return cfg;
22170     },
22171     
22172     update : function(aria_valuenow)
22173     {
22174         this.aria_valuenow = aria_valuenow;
22175         
22176         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22177     }
22178    
22179 });
22180
22181  
22182
22183  /**
22184  * @class Roo.bootstrap.TabGroup
22185  * @extends Roo.bootstrap.Column
22186  * @children Roo.bootstrap.TabPanel
22187  * Bootstrap Column class
22188  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22189  * @cfg {Boolean} carousel true to make the group behave like a carousel
22190  * @cfg {Boolean} bullets show bullets for the panels
22191  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22192  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22193  * @cfg {Boolean} showarrow (true|false) show arrow default true
22194  * 
22195  * @constructor
22196  * Create a new TabGroup
22197  * @param {Object} config The config object
22198  */
22199
22200 Roo.bootstrap.TabGroup = function(config){
22201     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22202     if (!this.navId) {
22203         this.navId = Roo.id();
22204     }
22205     this.tabs = [];
22206     Roo.bootstrap.TabGroup.register(this);
22207     
22208 };
22209
22210 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22211     
22212     carousel : false,
22213     transition : false,
22214     bullets : 0,
22215     timer : 0,
22216     autoslide : false,
22217     slideFn : false,
22218     slideOnTouch : false,
22219     showarrow : true,
22220     
22221     getAutoCreate : function()
22222     {
22223         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22224         
22225         cfg.cls += ' tab-content';
22226         
22227         if (this.carousel) {
22228             cfg.cls += ' carousel slide';
22229             
22230             cfg.cn = [{
22231                cls : 'carousel-inner',
22232                cn : []
22233             }];
22234         
22235             if(this.bullets  && !Roo.isTouch){
22236                 
22237                 var bullets = {
22238                     cls : 'carousel-bullets',
22239                     cn : []
22240                 };
22241                
22242                 if(this.bullets_cls){
22243                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22244                 }
22245                 
22246                 bullets.cn.push({
22247                     cls : 'clear'
22248                 });
22249                 
22250                 cfg.cn[0].cn.push(bullets);
22251             }
22252             
22253             if(this.showarrow){
22254                 cfg.cn[0].cn.push({
22255                     tag : 'div',
22256                     class : 'carousel-arrow',
22257                     cn : [
22258                         {
22259                             tag : 'div',
22260                             class : 'carousel-prev',
22261                             cn : [
22262                                 {
22263                                     tag : 'i',
22264                                     class : 'fa fa-chevron-left'
22265                                 }
22266                             ]
22267                         },
22268                         {
22269                             tag : 'div',
22270                             class : 'carousel-next',
22271                             cn : [
22272                                 {
22273                                     tag : 'i',
22274                                     class : 'fa fa-chevron-right'
22275                                 }
22276                             ]
22277                         }
22278                     ]
22279                 });
22280             }
22281             
22282         }
22283         
22284         return cfg;
22285     },
22286     
22287     initEvents:  function()
22288     {
22289 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22290 //            this.el.on("touchstart", this.onTouchStart, this);
22291 //        }
22292         
22293         if(this.autoslide){
22294             var _this = this;
22295             
22296             this.slideFn = window.setInterval(function() {
22297                 _this.showPanelNext();
22298             }, this.timer);
22299         }
22300         
22301         if(this.showarrow){
22302             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22303             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22304         }
22305         
22306         
22307     },
22308     
22309 //    onTouchStart : function(e, el, o)
22310 //    {
22311 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22312 //            return;
22313 //        }
22314 //        
22315 //        this.showPanelNext();
22316 //    },
22317     
22318     
22319     getChildContainer : function()
22320     {
22321         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22322     },
22323     
22324     /**
22325     * register a Navigation item
22326     * @param {Roo.bootstrap.nav.Item} the navitem to add
22327     */
22328     register : function(item)
22329     {
22330         this.tabs.push( item);
22331         item.navId = this.navId; // not really needed..
22332         this.addBullet();
22333     
22334     },
22335     
22336     getActivePanel : function()
22337     {
22338         var r = false;
22339         Roo.each(this.tabs, function(t) {
22340             if (t.active) {
22341                 r = t;
22342                 return false;
22343             }
22344             return null;
22345         });
22346         return r;
22347         
22348     },
22349     getPanelByName : function(n)
22350     {
22351         var r = false;
22352         Roo.each(this.tabs, function(t) {
22353             if (t.tabId == n) {
22354                 r = t;
22355                 return false;
22356             }
22357             return null;
22358         });
22359         return r;
22360     },
22361     indexOfPanel : function(p)
22362     {
22363         var r = false;
22364         Roo.each(this.tabs, function(t,i) {
22365             if (t.tabId == p.tabId) {
22366                 r = i;
22367                 return false;
22368             }
22369             return null;
22370         });
22371         return r;
22372     },
22373     /**
22374      * show a specific panel
22375      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22376      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22377      */
22378     showPanel : function (pan)
22379     {
22380         if(this.transition || typeof(pan) == 'undefined'){
22381             Roo.log("waiting for the transitionend");
22382             return false;
22383         }
22384         
22385         if (typeof(pan) == 'number') {
22386             pan = this.tabs[pan];
22387         }
22388         
22389         if (typeof(pan) == 'string') {
22390             pan = this.getPanelByName(pan);
22391         }
22392         
22393         var cur = this.getActivePanel();
22394         
22395         if(!pan || !cur){
22396             Roo.log('pan or acitve pan is undefined');
22397             return false;
22398         }
22399         
22400         if (pan.tabId == this.getActivePanel().tabId) {
22401             return true;
22402         }
22403         
22404         if (false === cur.fireEvent('beforedeactivate')) {
22405             return false;
22406         }
22407         
22408         if(this.bullets > 0 && !Roo.isTouch){
22409             this.setActiveBullet(this.indexOfPanel(pan));
22410         }
22411         
22412         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22413             
22414             //class="carousel-item carousel-item-next carousel-item-left"
22415             
22416             this.transition = true;
22417             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22418             var lr = dir == 'next' ? 'left' : 'right';
22419             pan.el.addClass(dir); // or prev
22420             pan.el.addClass('carousel-item-' + dir); // or prev
22421             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22422             cur.el.addClass(lr); // or right
22423             pan.el.addClass(lr);
22424             cur.el.addClass('carousel-item-' +lr); // or right
22425             pan.el.addClass('carousel-item-' +lr);
22426             
22427             
22428             var _this = this;
22429             cur.el.on('transitionend', function() {
22430                 Roo.log("trans end?");
22431                 
22432                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22433                 pan.setActive(true);
22434                 
22435                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22436                 cur.setActive(false);
22437                 
22438                 _this.transition = false;
22439                 
22440             }, this, { single:  true } );
22441             
22442             return true;
22443         }
22444         
22445         cur.setActive(false);
22446         pan.setActive(true);
22447         
22448         return true;
22449         
22450     },
22451     showPanelNext : function()
22452     {
22453         var i = this.indexOfPanel(this.getActivePanel());
22454         
22455         if (i >= this.tabs.length - 1 && !this.autoslide) {
22456             return;
22457         }
22458         
22459         if (i >= this.tabs.length - 1 && this.autoslide) {
22460             i = -1;
22461         }
22462         
22463         this.showPanel(this.tabs[i+1]);
22464     },
22465     
22466     showPanelPrev : function()
22467     {
22468         var i = this.indexOfPanel(this.getActivePanel());
22469         
22470         if (i  < 1 && !this.autoslide) {
22471             return;
22472         }
22473         
22474         if (i < 1 && this.autoslide) {
22475             i = this.tabs.length;
22476         }
22477         
22478         this.showPanel(this.tabs[i-1]);
22479     },
22480     
22481     
22482     addBullet: function()
22483     {
22484         if(!this.bullets || Roo.isTouch){
22485             return;
22486         }
22487         var ctr = this.el.select('.carousel-bullets',true).first();
22488         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22489         var bullet = ctr.createChild({
22490             cls : 'bullet bullet-' + i
22491         },ctr.dom.lastChild);
22492         
22493         
22494         var _this = this;
22495         
22496         bullet.on('click', (function(e, el, o, ii, t){
22497
22498             e.preventDefault();
22499
22500             this.showPanel(ii);
22501
22502             if(this.autoslide && this.slideFn){
22503                 clearInterval(this.slideFn);
22504                 this.slideFn = window.setInterval(function() {
22505                     _this.showPanelNext();
22506                 }, this.timer);
22507             }
22508
22509         }).createDelegate(this, [i, bullet], true));
22510                 
22511         
22512     },
22513      
22514     setActiveBullet : function(i)
22515     {
22516         if(Roo.isTouch){
22517             return;
22518         }
22519         
22520         Roo.each(this.el.select('.bullet', true).elements, function(el){
22521             el.removeClass('selected');
22522         });
22523
22524         var bullet = this.el.select('.bullet-' + i, true).first();
22525         
22526         if(!bullet){
22527             return;
22528         }
22529         
22530         bullet.addClass('selected');
22531     }
22532     
22533     
22534   
22535 });
22536
22537  
22538
22539  
22540  
22541 Roo.apply(Roo.bootstrap.TabGroup, {
22542     
22543     groups: {},
22544      /**
22545     * register a Navigation Group
22546     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22547     */
22548     register : function(navgrp)
22549     {
22550         this.groups[navgrp.navId] = navgrp;
22551         
22552     },
22553     /**
22554     * fetch a Navigation Group based on the navigation ID
22555     * if one does not exist , it will get created.
22556     * @param {string} the navgroup to add
22557     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22558     */
22559     get: function(navId) {
22560         if (typeof(this.groups[navId]) == 'undefined') {
22561             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22562         }
22563         return this.groups[navId] ;
22564     }
22565     
22566     
22567     
22568 });
22569
22570  /*
22571  * - LGPL
22572  *
22573  * TabPanel
22574  * 
22575  */
22576
22577 /**
22578  * @class Roo.bootstrap.TabPanel
22579  * @extends Roo.bootstrap.Component
22580  * @children Roo.bootstrap.Component
22581  * Bootstrap TabPanel class
22582  * @cfg {Boolean} active panel active
22583  * @cfg {String} html panel content
22584  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22585  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22586  * @cfg {String} href click to link..
22587  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22588  * 
22589  * 
22590  * @constructor
22591  * Create a new TabPanel
22592  * @param {Object} config The config object
22593  */
22594
22595 Roo.bootstrap.TabPanel = function(config){
22596     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22597     this.addEvents({
22598         /**
22599              * @event changed
22600              * Fires when the active status changes
22601              * @param {Roo.bootstrap.TabPanel} this
22602              * @param {Boolean} state the new state
22603             
22604          */
22605         'changed': true,
22606         /**
22607              * @event beforedeactivate
22608              * Fires before a tab is de-activated - can be used to do validation on a form.
22609              * @param {Roo.bootstrap.TabPanel} this
22610              * @return {Boolean} false if there is an error
22611             
22612          */
22613         'beforedeactivate': true
22614      });
22615     
22616     this.tabId = this.tabId || Roo.id();
22617   
22618 };
22619
22620 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22621     
22622     active: false,
22623     html: false,
22624     tabId: false,
22625     navId : false,
22626     href : '',
22627     touchSlide : false,
22628     getAutoCreate : function(){
22629         
22630         
22631         var cfg = {
22632             tag: 'div',
22633             // item is needed for carousel - not sure if it has any effect otherwise
22634             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22635             html: this.html || ''
22636         };
22637         
22638         if(this.active){
22639             cfg.cls += ' active';
22640         }
22641         
22642         if(this.tabId){
22643             cfg.tabId = this.tabId;
22644         }
22645         
22646         
22647         
22648         return cfg;
22649     },
22650     
22651     initEvents:  function()
22652     {
22653         var p = this.parent();
22654         
22655         this.navId = this.navId || p.navId;
22656         
22657         if (typeof(this.navId) != 'undefined') {
22658             // not really needed.. but just in case.. parent should be a NavGroup.
22659             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22660             
22661             tg.register(this);
22662             
22663             var i = tg.tabs.length - 1;
22664             
22665             if(this.active && tg.bullets > 0 && i < tg.bullets){
22666                 tg.setActiveBullet(i);
22667             }
22668         }
22669         
22670         this.el.on('click', this.onClick, this);
22671         
22672         if(Roo.isTouch && this.touchSlide){
22673             this.el.on("touchstart", this.onTouchStart, this);
22674             this.el.on("touchmove", this.onTouchMove, this);
22675             this.el.on("touchend", this.onTouchEnd, this);
22676         }
22677         
22678     },
22679     
22680     onRender : function(ct, position)
22681     {
22682         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22683     },
22684     
22685     setActive : function(state)
22686     {
22687         Roo.log("panel - set active " + this.tabId + "=" + state);
22688         
22689         this.active = state;
22690         if (!state) {
22691             this.el.removeClass('active');
22692             
22693         } else  if (!this.el.hasClass('active')) {
22694             this.el.addClass('active');
22695         }
22696         
22697         this.fireEvent('changed', this, state);
22698     },
22699     
22700     onClick : function(e)
22701     {
22702         e.preventDefault();
22703         
22704         if(!this.href.length){
22705             return;
22706         }
22707         
22708         window.location.href = this.href;
22709     },
22710     
22711     startX : 0,
22712     startY : 0,
22713     endX : 0,
22714     endY : 0,
22715     swiping : false,
22716     
22717     onTouchStart : function(e)
22718     {
22719         this.swiping = false;
22720         
22721         this.startX = e.browserEvent.touches[0].clientX;
22722         this.startY = e.browserEvent.touches[0].clientY;
22723     },
22724     
22725     onTouchMove : function(e)
22726     {
22727         this.swiping = true;
22728         
22729         this.endX = e.browserEvent.touches[0].clientX;
22730         this.endY = e.browserEvent.touches[0].clientY;
22731     },
22732     
22733     onTouchEnd : function(e)
22734     {
22735         if(!this.swiping){
22736             this.onClick(e);
22737             return;
22738         }
22739         
22740         var tabGroup = this.parent();
22741         
22742         if(this.endX > this.startX){ // swiping right
22743             tabGroup.showPanelPrev();
22744             return;
22745         }
22746         
22747         if(this.startX > this.endX){ // swiping left
22748             tabGroup.showPanelNext();
22749             return;
22750         }
22751     }
22752     
22753     
22754 });
22755  
22756
22757  
22758
22759  /*
22760  * - LGPL
22761  *
22762  * DateField
22763  * 
22764  */
22765
22766 /**
22767  * @class Roo.bootstrap.form.DateField
22768  * @extends Roo.bootstrap.form.Input
22769  * Bootstrap DateField class
22770  * @cfg {Number} weekStart default 0
22771  * @cfg {String} viewMode default empty, (months|years)
22772  * @cfg {String} minViewMode default empty, (months|years)
22773  * @cfg {Number} startDate default -Infinity
22774  * @cfg {Number} endDate default Infinity
22775  * @cfg {Boolean} todayHighlight default false
22776  * @cfg {Boolean} todayBtn default false
22777  * @cfg {Boolean} calendarWeeks default false
22778  * @cfg {Object} daysOfWeekDisabled default empty
22779  * @cfg {Boolean} singleMode default false (true | false)
22780  * 
22781  * @cfg {Boolean} keyboardNavigation default true
22782  * @cfg {String} language default en
22783  * 
22784  * @constructor
22785  * Create a new DateField
22786  * @param {Object} config The config object
22787  */
22788
22789 Roo.bootstrap.form.DateField = function(config){
22790     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22791      this.addEvents({
22792             /**
22793              * @event show
22794              * Fires when this field show.
22795              * @param {Roo.bootstrap.form.DateField} this
22796              * @param {Mixed} date The date value
22797              */
22798             show : true,
22799             /**
22800              * @event show
22801              * Fires when this field hide.
22802              * @param {Roo.bootstrap.form.DateField} this
22803              * @param {Mixed} date The date value
22804              */
22805             hide : true,
22806             /**
22807              * @event select
22808              * Fires when select a date.
22809              * @param {Roo.bootstrap.form.DateField} this
22810              * @param {Mixed} date The date value
22811              */
22812             select : true,
22813             /**
22814              * @event beforeselect
22815              * Fires when before select a date.
22816              * @param {Roo.bootstrap.form.DateField} this
22817              * @param {Mixed} date The date value
22818              */
22819             beforeselect : true
22820         });
22821 };
22822
22823 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22824     
22825     /**
22826      * @cfg {String} format
22827      * The default date format string which can be overriden for localization support.  The format must be
22828      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22829      */
22830     format : "m/d/y",
22831     /**
22832      * @cfg {String} altFormats
22833      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22834      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22835      */
22836     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22837     
22838     weekStart : 0,
22839     
22840     viewMode : '',
22841     
22842     minViewMode : '',
22843     
22844     todayHighlight : false,
22845     
22846     todayBtn: false,
22847     
22848     language: 'en',
22849     
22850     keyboardNavigation: true,
22851     
22852     calendarWeeks: false,
22853     
22854     startDate: -Infinity,
22855     
22856     endDate: Infinity,
22857     
22858     daysOfWeekDisabled: [],
22859     
22860     _events: [],
22861     
22862     singleMode : false,
22863     
22864     UTCDate: function()
22865     {
22866         return new Date(Date.UTC.apply(Date, arguments));
22867     },
22868     
22869     UTCToday: function()
22870     {
22871         var today = new Date();
22872         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22873     },
22874     
22875     getDate: function() {
22876             var d = this.getUTCDate();
22877             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22878     },
22879     
22880     getUTCDate: function() {
22881             return this.date;
22882     },
22883     
22884     setDate: function(d) {
22885             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22886     },
22887     
22888     setUTCDate: function(d) {
22889             this.date = d;
22890             this.setValue(this.formatDate(this.date));
22891     },
22892         
22893     onRender: function(ct, position)
22894     {
22895         
22896         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22897         
22898         this.language = this.language || 'en';
22899         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22900         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22901         
22902         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22903         this.format = this.format || 'm/d/y';
22904         this.isInline = false;
22905         this.isInput = true;
22906         this.component = this.el.select('.add-on', true).first() || false;
22907         this.component = (this.component && this.component.length === 0) ? false : this.component;
22908         this.hasInput = this.component && this.inputEl().length;
22909         
22910         if (typeof(this.minViewMode === 'string')) {
22911             switch (this.minViewMode) {
22912                 case 'months':
22913                     this.minViewMode = 1;
22914                     break;
22915                 case 'years':
22916                     this.minViewMode = 2;
22917                     break;
22918                 default:
22919                     this.minViewMode = 0;
22920                     break;
22921             }
22922         }
22923         
22924         if (typeof(this.viewMode === 'string')) {
22925             switch (this.viewMode) {
22926                 case 'months':
22927                     this.viewMode = 1;
22928                     break;
22929                 case 'years':
22930                     this.viewMode = 2;
22931                     break;
22932                 default:
22933                     this.viewMode = 0;
22934                     break;
22935             }
22936         }
22937                 
22938         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22939         
22940 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22941         
22942         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22943         
22944         this.picker().on('mousedown', this.onMousedown, this);
22945         this.picker().on('click', this.onClick, this);
22946         
22947         this.picker().addClass('datepicker-dropdown');
22948         
22949         this.startViewMode = this.viewMode;
22950         
22951         if(this.singleMode){
22952             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22953                 v.setVisibilityMode(Roo.Element.DISPLAY);
22954                 v.hide();
22955             });
22956             
22957             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22958                 v.setStyle('width', '189px');
22959             });
22960         }
22961         
22962         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22963             if(!this.calendarWeeks){
22964                 v.remove();
22965                 return;
22966             }
22967             
22968             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22969             v.attr('colspan', function(i, val){
22970                 return parseInt(val) + 1;
22971             });
22972         });
22973                         
22974         
22975         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22976         
22977         this.setStartDate(this.startDate);
22978         this.setEndDate(this.endDate);
22979         
22980         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22981         
22982         this.fillDow();
22983         this.fillMonths();
22984         this.update();
22985         this.showMode();
22986         
22987         if(this.isInline) {
22988             this.showPopup();
22989         }
22990     },
22991     
22992     picker : function()
22993     {
22994         return this.pickerEl;
22995 //        return this.el.select('.datepicker', true).first();
22996     },
22997     
22998     fillDow: function()
22999     {
23000         var dowCnt = this.weekStart;
23001         
23002         var dow = {
23003             tag: 'tr',
23004             cn: [
23005                 
23006             ]
23007         };
23008         
23009         if(this.calendarWeeks){
23010             dow.cn.push({
23011                 tag: 'th',
23012                 cls: 'cw',
23013                 html: '&nbsp;'
23014             })
23015         }
23016         
23017         while (dowCnt < this.weekStart + 7) {
23018             dow.cn.push({
23019                 tag: 'th',
23020                 cls: 'dow',
23021                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23022             });
23023         }
23024         
23025         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23026     },
23027     
23028     fillMonths: function()
23029     {    
23030         var i = 0;
23031         var months = this.picker().select('>.datepicker-months td', true).first();
23032         
23033         months.dom.innerHTML = '';
23034         
23035         while (i < 12) {
23036             var month = {
23037                 tag: 'span',
23038                 cls: 'month',
23039                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23040             };
23041             
23042             months.createChild(month);
23043         }
23044         
23045     },
23046     
23047     update: function()
23048     {
23049         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;
23050         
23051         if (this.date < this.startDate) {
23052             this.viewDate = new Date(this.startDate);
23053         } else if (this.date > this.endDate) {
23054             this.viewDate = new Date(this.endDate);
23055         } else {
23056             this.viewDate = new Date(this.date);
23057         }
23058         
23059         this.fill();
23060     },
23061     
23062     fill: function() 
23063     {
23064         var d = new Date(this.viewDate),
23065                 year = d.getUTCFullYear(),
23066                 month = d.getUTCMonth(),
23067                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23068                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23069                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23070                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23071                 currentDate = this.date && this.date.valueOf(),
23072                 today = this.UTCToday();
23073         
23074         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23075         
23076 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23077         
23078 //        this.picker.select('>tfoot th.today').
23079 //                                              .text(dates[this.language].today)
23080 //                                              .toggle(this.todayBtn !== false);
23081     
23082         this.updateNavArrows();
23083         this.fillMonths();
23084                                                 
23085         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23086         
23087         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23088          
23089         prevMonth.setUTCDate(day);
23090         
23091         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23092         
23093         var nextMonth = new Date(prevMonth);
23094         
23095         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23096         
23097         nextMonth = nextMonth.valueOf();
23098         
23099         var fillMonths = false;
23100         
23101         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23102         
23103         while(prevMonth.valueOf() <= nextMonth) {
23104             var clsName = '';
23105             
23106             if (prevMonth.getUTCDay() === this.weekStart) {
23107                 if(fillMonths){
23108                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23109                 }
23110                     
23111                 fillMonths = {
23112                     tag: 'tr',
23113                     cn: []
23114                 };
23115                 
23116                 if(this.calendarWeeks){
23117                     // ISO 8601: First week contains first thursday.
23118                     // ISO also states week starts on Monday, but we can be more abstract here.
23119                     var
23120                     // Start of current week: based on weekstart/current date
23121                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23122                     // Thursday of this week
23123                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23124                     // First Thursday of year, year from thursday
23125                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23126                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23127                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23128                     
23129                     fillMonths.cn.push({
23130                         tag: 'td',
23131                         cls: 'cw',
23132                         html: calWeek
23133                     });
23134                 }
23135             }
23136             
23137             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23138                 clsName += ' old';
23139             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23140                 clsName += ' new';
23141             }
23142             if (this.todayHighlight &&
23143                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23144                 prevMonth.getUTCMonth() == today.getMonth() &&
23145                 prevMonth.getUTCDate() == today.getDate()) {
23146                 clsName += ' today';
23147             }
23148             
23149             if (currentDate && prevMonth.valueOf() === currentDate) {
23150                 clsName += ' active';
23151             }
23152             
23153             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23154                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23155                     clsName += ' disabled';
23156             }
23157             
23158             fillMonths.cn.push({
23159                 tag: 'td',
23160                 cls: 'day ' + clsName,
23161                 html: prevMonth.getDate()
23162             });
23163             
23164             prevMonth.setDate(prevMonth.getDate()+1);
23165         }
23166           
23167         var currentYear = this.date && this.date.getUTCFullYear();
23168         var currentMonth = this.date && this.date.getUTCMonth();
23169         
23170         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23171         
23172         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23173             v.removeClass('active');
23174             
23175             if(currentYear === year && k === currentMonth){
23176                 v.addClass('active');
23177             }
23178             
23179             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23180                 v.addClass('disabled');
23181             }
23182             
23183         });
23184         
23185         
23186         year = parseInt(year/10, 10) * 10;
23187         
23188         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23189         
23190         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23191         
23192         year -= 1;
23193         for (var i = -1; i < 11; i++) {
23194             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23195                 tag: 'span',
23196                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23197                 html: year
23198             });
23199             
23200             year += 1;
23201         }
23202     },
23203     
23204     showMode: function(dir) 
23205     {
23206         if (dir) {
23207             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23208         }
23209         
23210         Roo.each(this.picker().select('>div',true).elements, function(v){
23211             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23212             v.hide();
23213         });
23214         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23215     },
23216     
23217     place: function()
23218     {
23219         if(this.isInline) {
23220             return;
23221         }
23222         
23223         this.picker().removeClass(['bottom', 'top']);
23224         
23225         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23226             /*
23227              * place to the top of element!
23228              *
23229              */
23230             
23231             this.picker().addClass('top');
23232             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23233             
23234             return;
23235         }
23236         
23237         this.picker().addClass('bottom');
23238         
23239         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23240     },
23241     
23242     parseDate : function(value)
23243     {
23244         if(!value || value instanceof Date){
23245             return value;
23246         }
23247         var v = Date.parseDate(value, this.format);
23248         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23249             v = Date.parseDate(value, 'Y-m-d');
23250         }
23251         if(!v && this.altFormats){
23252             if(!this.altFormatsArray){
23253                 this.altFormatsArray = this.altFormats.split("|");
23254             }
23255             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23256                 v = Date.parseDate(value, this.altFormatsArray[i]);
23257             }
23258         }
23259         return v;
23260     },
23261     
23262     formatDate : function(date, fmt)
23263     {   
23264         return (!date || !(date instanceof Date)) ?
23265         date : date.dateFormat(fmt || this.format);
23266     },
23267     
23268     onFocus : function()
23269     {
23270         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23271         this.showPopup();
23272     },
23273     
23274     onBlur : function()
23275     {
23276         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23277         
23278         var d = this.inputEl().getValue();
23279         
23280         this.setValue(d);
23281                 
23282         this.hidePopup();
23283     },
23284     
23285     showPopup : function()
23286     {
23287         this.picker().show();
23288         this.update();
23289         this.place();
23290         
23291         this.fireEvent('showpopup', this, this.date);
23292     },
23293     
23294     hidePopup : function()
23295     {
23296         if(this.isInline) {
23297             return;
23298         }
23299         this.picker().hide();
23300         this.viewMode = this.startViewMode;
23301         this.showMode();
23302         
23303         this.fireEvent('hidepopup', this, this.date);
23304         
23305     },
23306     
23307     onMousedown: function(e)
23308     {
23309         e.stopPropagation();
23310         e.preventDefault();
23311     },
23312     
23313     keyup: function(e)
23314     {
23315         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23316         this.update();
23317     },
23318
23319     setValue: function(v)
23320     {
23321         if(this.fireEvent('beforeselect', this, v) !== false){
23322             var d = new Date(this.parseDate(v) ).clearTime();
23323         
23324             if(isNaN(d.getTime())){
23325                 this.date = this.viewDate = '';
23326                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23327                 return;
23328             }
23329
23330             v = this.formatDate(d);
23331
23332             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23333
23334             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23335
23336             this.update();
23337
23338             this.fireEvent('select', this, this.date);
23339         }
23340     },
23341     
23342     getValue: function()
23343     {
23344         return this.formatDate(this.date);
23345     },
23346     
23347     fireKey: function(e)
23348     {
23349         if (!this.picker().isVisible()){
23350             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23351                 this.showPopup();
23352             }
23353             return;
23354         }
23355         
23356         var dateChanged = false,
23357         dir, day, month,
23358         newDate, newViewDate;
23359         
23360         switch(e.keyCode){
23361             case 27: // escape
23362                 this.hidePopup();
23363                 e.preventDefault();
23364                 break;
23365             case 37: // left
23366             case 39: // right
23367                 if (!this.keyboardNavigation) {
23368                     break;
23369                 }
23370                 dir = e.keyCode == 37 ? -1 : 1;
23371                 
23372                 if (e.ctrlKey){
23373                     newDate = this.moveYear(this.date, dir);
23374                     newViewDate = this.moveYear(this.viewDate, dir);
23375                 } else if (e.shiftKey){
23376                     newDate = this.moveMonth(this.date, dir);
23377                     newViewDate = this.moveMonth(this.viewDate, dir);
23378                 } else {
23379                     newDate = new Date(this.date);
23380                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23381                     newViewDate = new Date(this.viewDate);
23382                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23383                 }
23384                 if (this.dateWithinRange(newDate)){
23385                     this.date = newDate;
23386                     this.viewDate = newViewDate;
23387                     this.setValue(this.formatDate(this.date));
23388 //                    this.update();
23389                     e.preventDefault();
23390                     dateChanged = true;
23391                 }
23392                 break;
23393             case 38: // up
23394             case 40: // down
23395                 if (!this.keyboardNavigation) {
23396                     break;
23397                 }
23398                 dir = e.keyCode == 38 ? -1 : 1;
23399                 if (e.ctrlKey){
23400                     newDate = this.moveYear(this.date, dir);
23401                     newViewDate = this.moveYear(this.viewDate, dir);
23402                 } else if (e.shiftKey){
23403                     newDate = this.moveMonth(this.date, dir);
23404                     newViewDate = this.moveMonth(this.viewDate, dir);
23405                 } else {
23406                     newDate = new Date(this.date);
23407                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23408                     newViewDate = new Date(this.viewDate);
23409                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23410                 }
23411                 if (this.dateWithinRange(newDate)){
23412                     this.date = newDate;
23413                     this.viewDate = newViewDate;
23414                     this.setValue(this.formatDate(this.date));
23415 //                    this.update();
23416                     e.preventDefault();
23417                     dateChanged = true;
23418                 }
23419                 break;
23420             case 13: // enter
23421                 this.setValue(this.formatDate(this.date));
23422                 this.hidePopup();
23423                 e.preventDefault();
23424                 break;
23425             case 9: // tab
23426                 this.setValue(this.formatDate(this.date));
23427                 this.hidePopup();
23428                 break;
23429             case 16: // shift
23430             case 17: // ctrl
23431             case 18: // alt
23432                 break;
23433             default :
23434                 this.hidePopup();
23435                 
23436         }
23437     },
23438     
23439     
23440     onClick: function(e) 
23441     {
23442         e.stopPropagation();
23443         e.preventDefault();
23444         
23445         var target = e.getTarget();
23446         
23447         if(target.nodeName.toLowerCase() === 'i'){
23448             target = Roo.get(target).dom.parentNode;
23449         }
23450         
23451         var nodeName = target.nodeName;
23452         var className = target.className;
23453         var html = target.innerHTML;
23454         //Roo.log(nodeName);
23455         
23456         switch(nodeName.toLowerCase()) {
23457             case 'th':
23458                 switch(className) {
23459                     case 'switch':
23460                         this.showMode(1);
23461                         break;
23462                     case 'prev':
23463                     case 'next':
23464                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23465                         switch(this.viewMode){
23466                                 case 0:
23467                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23468                                         break;
23469                                 case 1:
23470                                 case 2:
23471                                         this.viewDate = this.moveYear(this.viewDate, dir);
23472                                         break;
23473                         }
23474                         this.fill();
23475                         break;
23476                     case 'today':
23477                         var date = new Date();
23478                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23479 //                        this.fill()
23480                         this.setValue(this.formatDate(this.date));
23481                         
23482                         this.hidePopup();
23483                         break;
23484                 }
23485                 break;
23486             case 'span':
23487                 if (className.indexOf('disabled') < 0) {
23488                 if (!this.viewDate) {
23489                     this.viewDate = new Date();
23490                 }
23491                 this.viewDate.setUTCDate(1);
23492                     if (className.indexOf('month') > -1) {
23493                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23494                     } else {
23495                         var year = parseInt(html, 10) || 0;
23496                         this.viewDate.setUTCFullYear(year);
23497                         
23498                     }
23499                     
23500                     if(this.singleMode){
23501                         this.setValue(this.formatDate(this.viewDate));
23502                         this.hidePopup();
23503                         return;
23504                     }
23505                     
23506                     this.showMode(-1);
23507                     this.fill();
23508                 }
23509                 break;
23510                 
23511             case 'td':
23512                 //Roo.log(className);
23513                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23514                     var day = parseInt(html, 10) || 1;
23515                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23516                         month = (this.viewDate || new Date()).getUTCMonth();
23517
23518                     if (className.indexOf('old') > -1) {
23519                         if(month === 0 ){
23520                             month = 11;
23521                             year -= 1;
23522                         }else{
23523                             month -= 1;
23524                         }
23525                     } else if (className.indexOf('new') > -1) {
23526                         if (month == 11) {
23527                             month = 0;
23528                             year += 1;
23529                         } else {
23530                             month += 1;
23531                         }
23532                     }
23533                     //Roo.log([year,month,day]);
23534                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23535                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23536 //                    this.fill();
23537                     //Roo.log(this.formatDate(this.date));
23538                     this.setValue(this.formatDate(this.date));
23539                     this.hidePopup();
23540                 }
23541                 break;
23542         }
23543     },
23544     
23545     setStartDate: function(startDate)
23546     {
23547         this.startDate = startDate || -Infinity;
23548         if (this.startDate !== -Infinity) {
23549             this.startDate = this.parseDate(this.startDate);
23550         }
23551         this.update();
23552         this.updateNavArrows();
23553     },
23554
23555     setEndDate: function(endDate)
23556     {
23557         this.endDate = endDate || Infinity;
23558         if (this.endDate !== Infinity) {
23559             this.endDate = this.parseDate(this.endDate);
23560         }
23561         this.update();
23562         this.updateNavArrows();
23563     },
23564     
23565     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23566     {
23567         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23568         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23569             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23570         }
23571         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23572             return parseInt(d, 10);
23573         });
23574         this.update();
23575         this.updateNavArrows();
23576     },
23577     
23578     updateNavArrows: function() 
23579     {
23580         if(this.singleMode){
23581             return;
23582         }
23583         
23584         var d = new Date(this.viewDate),
23585         year = d.getUTCFullYear(),
23586         month = d.getUTCMonth();
23587         
23588         Roo.each(this.picker().select('.prev', true).elements, function(v){
23589             v.show();
23590             switch (this.viewMode) {
23591                 case 0:
23592
23593                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23594                         v.hide();
23595                     }
23596                     break;
23597                 case 1:
23598                 case 2:
23599                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23600                         v.hide();
23601                     }
23602                     break;
23603             }
23604         });
23605         
23606         Roo.each(this.picker().select('.next', true).elements, function(v){
23607             v.show();
23608             switch (this.viewMode) {
23609                 case 0:
23610
23611                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23612                         v.hide();
23613                     }
23614                     break;
23615                 case 1:
23616                 case 2:
23617                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23618                         v.hide();
23619                     }
23620                     break;
23621             }
23622         })
23623     },
23624     
23625     moveMonth: function(date, dir)
23626     {
23627         if (!dir) {
23628             return date;
23629         }
23630         var new_date = new Date(date.valueOf()),
23631         day = new_date.getUTCDate(),
23632         month = new_date.getUTCMonth(),
23633         mag = Math.abs(dir),
23634         new_month, test;
23635         dir = dir > 0 ? 1 : -1;
23636         if (mag == 1){
23637             test = dir == -1
23638             // If going back one month, make sure month is not current month
23639             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23640             ? function(){
23641                 return new_date.getUTCMonth() == month;
23642             }
23643             // If going forward one month, make sure month is as expected
23644             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23645             : function(){
23646                 return new_date.getUTCMonth() != new_month;
23647             };
23648             new_month = month + dir;
23649             new_date.setUTCMonth(new_month);
23650             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23651             if (new_month < 0 || new_month > 11) {
23652                 new_month = (new_month + 12) % 12;
23653             }
23654         } else {
23655             // For magnitudes >1, move one month at a time...
23656             for (var i=0; i<mag; i++) {
23657                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23658                 new_date = this.moveMonth(new_date, dir);
23659             }
23660             // ...then reset the day, keeping it in the new month
23661             new_month = new_date.getUTCMonth();
23662             new_date.setUTCDate(day);
23663             test = function(){
23664                 return new_month != new_date.getUTCMonth();
23665             };
23666         }
23667         // Common date-resetting loop -- if date is beyond end of month, make it
23668         // end of month
23669         while (test()){
23670             new_date.setUTCDate(--day);
23671             new_date.setUTCMonth(new_month);
23672         }
23673         return new_date;
23674     },
23675
23676     moveYear: function(date, dir)
23677     {
23678         return this.moveMonth(date, dir*12);
23679     },
23680
23681     dateWithinRange: function(date)
23682     {
23683         return date >= this.startDate && date <= this.endDate;
23684     },
23685
23686     
23687     remove: function() 
23688     {
23689         this.picker().remove();
23690     },
23691     
23692     validateValue : function(value)
23693     {
23694         if(this.getVisibilityEl().hasClass('hidden')){
23695             return true;
23696         }
23697         
23698         if(value.length < 1)  {
23699             if(this.allowBlank){
23700                 return true;
23701             }
23702             return false;
23703         }
23704         
23705         if(value.length < this.minLength){
23706             return false;
23707         }
23708         if(value.length > this.maxLength){
23709             return false;
23710         }
23711         if(this.vtype){
23712             var vt = Roo.form.VTypes;
23713             if(!vt[this.vtype](value, this)){
23714                 return false;
23715             }
23716         }
23717         if(typeof this.validator == "function"){
23718             var msg = this.validator(value);
23719             if(msg !== true){
23720                 return false;
23721             }
23722         }
23723         
23724         if(this.regex && !this.regex.test(value)){
23725             return false;
23726         }
23727         
23728         if(typeof(this.parseDate(value)) == 'undefined'){
23729             return false;
23730         }
23731         
23732         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23733             return false;
23734         }      
23735         
23736         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23737             return false;
23738         } 
23739         
23740         
23741         return true;
23742     },
23743     
23744     reset : function()
23745     {
23746         this.date = this.viewDate = '';
23747         
23748         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23749     }
23750    
23751 });
23752
23753 Roo.apply(Roo.bootstrap.form.DateField,  {
23754     
23755     head : {
23756         tag: 'thead',
23757         cn: [
23758         {
23759             tag: 'tr',
23760             cn: [
23761             {
23762                 tag: 'th',
23763                 cls: 'prev',
23764                 html: '<i class="fa fa-arrow-left"/>'
23765             },
23766             {
23767                 tag: 'th',
23768                 cls: 'switch',
23769                 colspan: '5'
23770             },
23771             {
23772                 tag: 'th',
23773                 cls: 'next',
23774                 html: '<i class="fa fa-arrow-right"/>'
23775             }
23776
23777             ]
23778         }
23779         ]
23780     },
23781     
23782     content : {
23783         tag: 'tbody',
23784         cn: [
23785         {
23786             tag: 'tr',
23787             cn: [
23788             {
23789                 tag: 'td',
23790                 colspan: '7'
23791             }
23792             ]
23793         }
23794         ]
23795     },
23796     
23797     footer : {
23798         tag: 'tfoot',
23799         cn: [
23800         {
23801             tag: 'tr',
23802             cn: [
23803             {
23804                 tag: 'th',
23805                 colspan: '7',
23806                 cls: 'today'
23807             }
23808                     
23809             ]
23810         }
23811         ]
23812     },
23813     
23814     dates:{
23815         en: {
23816             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23817             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23818             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23819             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23820             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23821             today: "Today"
23822         }
23823     },
23824     
23825     modes: [
23826     {
23827         clsName: 'days',
23828         navFnc: 'Month',
23829         navStep: 1
23830     },
23831     {
23832         clsName: 'months',
23833         navFnc: 'FullYear',
23834         navStep: 1
23835     },
23836     {
23837         clsName: 'years',
23838         navFnc: 'FullYear',
23839         navStep: 10
23840     }]
23841 });
23842
23843 Roo.apply(Roo.bootstrap.form.DateField,  {
23844   
23845     template : {
23846         tag: 'div',
23847         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23848         cn: [
23849         {
23850             tag: 'div',
23851             cls: 'datepicker-days',
23852             cn: [
23853             {
23854                 tag: 'table',
23855                 cls: 'table-condensed',
23856                 cn:[
23857                 Roo.bootstrap.form.DateField.head,
23858                 {
23859                     tag: 'tbody'
23860                 },
23861                 Roo.bootstrap.form.DateField.footer
23862                 ]
23863             }
23864             ]
23865         },
23866         {
23867             tag: 'div',
23868             cls: 'datepicker-months',
23869             cn: [
23870             {
23871                 tag: 'table',
23872                 cls: 'table-condensed',
23873                 cn:[
23874                 Roo.bootstrap.form.DateField.head,
23875                 Roo.bootstrap.form.DateField.content,
23876                 Roo.bootstrap.form.DateField.footer
23877                 ]
23878             }
23879             ]
23880         },
23881         {
23882             tag: 'div',
23883             cls: 'datepicker-years',
23884             cn: [
23885             {
23886                 tag: 'table',
23887                 cls: 'table-condensed',
23888                 cn:[
23889                 Roo.bootstrap.form.DateField.head,
23890                 Roo.bootstrap.form.DateField.content,
23891                 Roo.bootstrap.form.DateField.footer
23892                 ]
23893             }
23894             ]
23895         }
23896         ]
23897     }
23898 });
23899
23900  
23901
23902  /*
23903  * - LGPL
23904  *
23905  * TimeField
23906  * 
23907  */
23908
23909 /**
23910  * @class Roo.bootstrap.form.TimeField
23911  * @extends Roo.bootstrap.form.Input
23912  * Bootstrap DateField class
23913  * 
23914  * 
23915  * @constructor
23916  * Create a new TimeField
23917  * @param {Object} config The config object
23918  */
23919
23920 Roo.bootstrap.form.TimeField = function(config){
23921     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23922     this.addEvents({
23923             /**
23924              * @event show
23925              * Fires when this field show.
23926              * @param {Roo.bootstrap.form.DateField} thisthis
23927              * @param {Mixed} date The date value
23928              */
23929             show : true,
23930             /**
23931              * @event show
23932              * Fires when this field hide.
23933              * @param {Roo.bootstrap.form.DateField} this
23934              * @param {Mixed} date The date value
23935              */
23936             hide : true,
23937             /**
23938              * @event select
23939              * Fires when select a date.
23940              * @param {Roo.bootstrap.form.DateField} this
23941              * @param {Mixed} date The date value
23942              */
23943             select : true
23944         });
23945 };
23946
23947 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23948     
23949     /**
23950      * @cfg {String} format
23951      * The default time format string which can be overriden for localization support.  The format must be
23952      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23953      */
23954     format : "H:i",
23955
23956     getAutoCreate : function()
23957     {
23958         this.after = '<i class="fa far fa-clock"></i>';
23959         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23960         
23961          
23962     },
23963     onRender: function(ct, position)
23964     {
23965         
23966         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23967                 
23968         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23969         
23970         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23971         
23972         this.pop = this.picker().select('>.datepicker-time',true).first();
23973         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23974         
23975         this.picker().on('mousedown', this.onMousedown, this);
23976         this.picker().on('click', this.onClick, this);
23977         
23978         this.picker().addClass('datepicker-dropdown');
23979     
23980         this.fillTime();
23981         this.update();
23982             
23983         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23984         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23985         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23986         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23987         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23988         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23989
23990     },
23991     
23992     fireKey: function(e){
23993         if (!this.picker().isVisible()){
23994             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23995                 this.show();
23996             }
23997             return;
23998         }
23999
24000         e.preventDefault();
24001         
24002         switch(e.keyCode){
24003             case 27: // escape
24004                 this.hide();
24005                 break;
24006             case 37: // left
24007             case 39: // right
24008                 this.onTogglePeriod();
24009                 break;
24010             case 38: // up
24011                 this.onIncrementMinutes();
24012                 break;
24013             case 40: // down
24014                 this.onDecrementMinutes();
24015                 break;
24016             case 13: // enter
24017             case 9: // tab
24018                 this.setTime();
24019                 break;
24020         }
24021     },
24022     
24023     onClick: function(e) {
24024         e.stopPropagation();
24025         e.preventDefault();
24026     },
24027     
24028     picker : function()
24029     {
24030         return this.pickerEl;
24031     },
24032     
24033     fillTime: function()
24034     {    
24035         var time = this.pop.select('tbody', true).first();
24036         
24037         time.dom.innerHTML = '';
24038         
24039         time.createChild({
24040             tag: 'tr',
24041             cn: [
24042                 {
24043                     tag: 'td',
24044                     cn: [
24045                         {
24046                             tag: 'a',
24047                             href: '#',
24048                             cls: 'btn',
24049                             cn: [
24050                                 {
24051                                     tag: 'i',
24052                                     cls: 'hours-up fa fas fa-chevron-up'
24053                                 }
24054                             ]
24055                         } 
24056                     ]
24057                 },
24058                 {
24059                     tag: 'td',
24060                     cls: 'separator'
24061                 },
24062                 {
24063                     tag: 'td',
24064                     cn: [
24065                         {
24066                             tag: 'a',
24067                             href: '#',
24068                             cls: 'btn',
24069                             cn: [
24070                                 {
24071                                     tag: 'i',
24072                                     cls: 'minutes-up fa fas fa-chevron-up'
24073                                 }
24074                             ]
24075                         }
24076                     ]
24077                 },
24078                 {
24079                     tag: 'td',
24080                     cls: 'separator'
24081                 }
24082             ]
24083         });
24084         
24085         time.createChild({
24086             tag: 'tr',
24087             cn: [
24088                 {
24089                     tag: 'td',
24090                     cn: [
24091                         {
24092                             tag: 'span',
24093                             cls: 'timepicker-hour',
24094                             html: '00'
24095                         }  
24096                     ]
24097                 },
24098                 {
24099                     tag: 'td',
24100                     cls: 'separator',
24101                     html: ':'
24102                 },
24103                 {
24104                     tag: 'td',
24105                     cn: [
24106                         {
24107                             tag: 'span',
24108                             cls: 'timepicker-minute',
24109                             html: '00'
24110                         }  
24111                     ]
24112                 },
24113                 {
24114                     tag: 'td',
24115                     cls: 'separator'
24116                 },
24117                 {
24118                     tag: 'td',
24119                     cn: [
24120                         {
24121                             tag: 'button',
24122                             type: 'button',
24123                             cls: 'btn btn-primary period',
24124                             html: 'AM'
24125                             
24126                         }
24127                     ]
24128                 }
24129             ]
24130         });
24131         
24132         time.createChild({
24133             tag: 'tr',
24134             cn: [
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'a',
24140                             href: '#',
24141                             cls: 'btn',
24142                             cn: [
24143                                 {
24144                                     tag: 'span',
24145                                     cls: 'hours-down fa fas fa-chevron-down'
24146                                 }
24147                             ]
24148                         }
24149                     ]
24150                 },
24151                 {
24152                     tag: 'td',
24153                     cls: 'separator'
24154                 },
24155                 {
24156                     tag: 'td',
24157                     cn: [
24158                         {
24159                             tag: 'a',
24160                             href: '#',
24161                             cls: 'btn',
24162                             cn: [
24163                                 {
24164                                     tag: 'span',
24165                                     cls: 'minutes-down fa fas fa-chevron-down'
24166                                 }
24167                             ]
24168                         }
24169                     ]
24170                 },
24171                 {
24172                     tag: 'td',
24173                     cls: 'separator'
24174                 }
24175             ]
24176         });
24177         
24178     },
24179     
24180     update: function()
24181     {
24182         
24183         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24184         
24185         this.fill();
24186     },
24187     
24188     fill: function() 
24189     {
24190         var hours = this.time.getHours();
24191         var minutes = this.time.getMinutes();
24192         var period = 'AM';
24193         
24194         if(hours > 11){
24195             period = 'PM';
24196         }
24197         
24198         if(hours == 0){
24199             hours = 12;
24200         }
24201         
24202         
24203         if(hours > 12){
24204             hours = hours - 12;
24205         }
24206         
24207         if(hours < 10){
24208             hours = '0' + hours;
24209         }
24210         
24211         if(minutes < 10){
24212             minutes = '0' + minutes;
24213         }
24214         
24215         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24216         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24217         this.pop.select('button', true).first().dom.innerHTML = period;
24218         
24219     },
24220     
24221     place: function()
24222     {   
24223         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24224         
24225         var cls = ['bottom'];
24226         
24227         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24228             cls.pop();
24229             cls.push('top');
24230         }
24231         
24232         cls.push('right');
24233         
24234         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24235             cls.pop();
24236             cls.push('left');
24237         }
24238         //this.picker().setXY(20000,20000);
24239         this.picker().addClass(cls.join('-'));
24240         
24241         var _this = this;
24242         
24243         Roo.each(cls, function(c){
24244             if(c == 'bottom'){
24245                 (function() {
24246                  //  
24247                 }).defer(200);
24248                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24249                 //_this.picker().setTop(_this.inputEl().getHeight());
24250                 return;
24251             }
24252             if(c == 'top'){
24253                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24254                 
24255                 //_this.picker().setTop(0 - _this.picker().getHeight());
24256                 return;
24257             }
24258             /*
24259             if(c == 'left'){
24260                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24261                 return;
24262             }
24263             if(c == 'right'){
24264                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24265                 return;
24266             }
24267             */
24268         });
24269         
24270     },
24271   
24272     onFocus : function()
24273     {
24274         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24275         this.show();
24276     },
24277     
24278     onBlur : function()
24279     {
24280         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24281         this.hide();
24282     },
24283     
24284     show : function()
24285     {
24286         this.picker().show();
24287         this.pop.show();
24288         this.update();
24289         this.place();
24290         
24291         this.fireEvent('show', this, this.date);
24292     },
24293     
24294     hide : function()
24295     {
24296         this.picker().hide();
24297         this.pop.hide();
24298         
24299         this.fireEvent('hide', this, this.date);
24300     },
24301     
24302     setTime : function()
24303     {
24304         this.hide();
24305         this.setValue(this.time.format(this.format));
24306         
24307         this.fireEvent('select', this, this.date);
24308         
24309         
24310     },
24311     
24312     onMousedown: function(e){
24313         e.stopPropagation();
24314         e.preventDefault();
24315     },
24316     
24317     onIncrementHours: function()
24318     {
24319         Roo.log('onIncrementHours');
24320         this.time = this.time.add(Date.HOUR, 1);
24321         this.update();
24322         
24323     },
24324     
24325     onDecrementHours: function()
24326     {
24327         Roo.log('onDecrementHours');
24328         this.time = this.time.add(Date.HOUR, -1);
24329         this.update();
24330     },
24331     
24332     onIncrementMinutes: function()
24333     {
24334         Roo.log('onIncrementMinutes');
24335         this.time = this.time.add(Date.MINUTE, 1);
24336         this.update();
24337     },
24338     
24339     onDecrementMinutes: function()
24340     {
24341         Roo.log('onDecrementMinutes');
24342         this.time = this.time.add(Date.MINUTE, -1);
24343         this.update();
24344     },
24345     
24346     onTogglePeriod: function()
24347     {
24348         Roo.log('onTogglePeriod');
24349         this.time = this.time.add(Date.HOUR, 12);
24350         this.update();
24351     }
24352     
24353    
24354 });
24355  
24356
24357 Roo.apply(Roo.bootstrap.form.TimeField,  {
24358   
24359     template : {
24360         tag: 'div',
24361         cls: 'datepicker dropdown-menu',
24362         cn: [
24363             {
24364                 tag: 'div',
24365                 cls: 'datepicker-time',
24366                 cn: [
24367                 {
24368                     tag: 'table',
24369                     cls: 'table-condensed',
24370                     cn:[
24371                         {
24372                             tag: 'tbody',
24373                             cn: [
24374                                 {
24375                                     tag: 'tr',
24376                                     cn: [
24377                                     {
24378                                         tag: 'td',
24379                                         colspan: '7'
24380                                     }
24381                                     ]
24382                                 }
24383                             ]
24384                         },
24385                         {
24386                             tag: 'tfoot',
24387                             cn: [
24388                                 {
24389                                     tag: 'tr',
24390                                     cn: [
24391                                     {
24392                                         tag: 'th',
24393                                         colspan: '7',
24394                                         cls: '',
24395                                         cn: [
24396                                             {
24397                                                 tag: 'button',
24398                                                 cls: 'btn btn-info ok',
24399                                                 html: 'OK'
24400                                             }
24401                                         ]
24402                                     }
24403                     
24404                                     ]
24405                                 }
24406                             ]
24407                         }
24408                     ]
24409                 }
24410                 ]
24411             }
24412         ]
24413     }
24414 });
24415
24416  
24417
24418  /*
24419  * - LGPL
24420  *
24421  * MonthField
24422  * 
24423  */
24424
24425 /**
24426  * @class Roo.bootstrap.form.MonthField
24427  * @extends Roo.bootstrap.form.Input
24428  * Bootstrap MonthField class
24429  * 
24430  * @cfg {String} language default en
24431  * 
24432  * @constructor
24433  * Create a new MonthField
24434  * @param {Object} config The config object
24435  */
24436
24437 Roo.bootstrap.form.MonthField = function(config){
24438     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24439     
24440     this.addEvents({
24441         /**
24442          * @event show
24443          * Fires when this field show.
24444          * @param {Roo.bootstrap.form.MonthField} this
24445          * @param {Mixed} date The date value
24446          */
24447         show : true,
24448         /**
24449          * @event show
24450          * Fires when this field hide.
24451          * @param {Roo.bootstrap.form.MonthField} this
24452          * @param {Mixed} date The date value
24453          */
24454         hide : true,
24455         /**
24456          * @event select
24457          * Fires when select a date.
24458          * @param {Roo.bootstrap.form.MonthField} this
24459          * @param {String} oldvalue The old value
24460          * @param {String} newvalue The new value
24461          */
24462         select : true
24463     });
24464 };
24465
24466 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24467     
24468     onRender: function(ct, position)
24469     {
24470         
24471         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24472         
24473         this.language = this.language || 'en';
24474         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24475         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24476         
24477         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24478         this.isInline = false;
24479         this.isInput = true;
24480         this.component = this.el.select('.add-on', true).first() || false;
24481         this.component = (this.component && this.component.length === 0) ? false : this.component;
24482         this.hasInput = this.component && this.inputEL().length;
24483         
24484         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24485         
24486         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24487         
24488         this.picker().on('mousedown', this.onMousedown, this);
24489         this.picker().on('click', this.onClick, this);
24490         
24491         this.picker().addClass('datepicker-dropdown');
24492         
24493         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24494             v.setStyle('width', '189px');
24495         });
24496         
24497         this.fillMonths();
24498         
24499         this.update();
24500         
24501         if(this.isInline) {
24502             this.show();
24503         }
24504         
24505     },
24506     
24507     setValue: function(v, suppressEvent)
24508     {   
24509         var o = this.getValue();
24510         
24511         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24512         
24513         this.update();
24514
24515         if(suppressEvent !== true){
24516             this.fireEvent('select', this, o, v);
24517         }
24518         
24519     },
24520     
24521     getValue: function()
24522     {
24523         return this.value;
24524     },
24525     
24526     onClick: function(e) 
24527     {
24528         e.stopPropagation();
24529         e.preventDefault();
24530         
24531         var target = e.getTarget();
24532         
24533         if(target.nodeName.toLowerCase() === 'i'){
24534             target = Roo.get(target).dom.parentNode;
24535         }
24536         
24537         var nodeName = target.nodeName;
24538         var className = target.className;
24539         var html = target.innerHTML;
24540         
24541         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24542             return;
24543         }
24544         
24545         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24546         
24547         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24548         
24549         this.hide();
24550                         
24551     },
24552     
24553     picker : function()
24554     {
24555         return this.pickerEl;
24556     },
24557     
24558     fillMonths: function()
24559     {    
24560         var i = 0;
24561         var months = this.picker().select('>.datepicker-months td', true).first();
24562         
24563         months.dom.innerHTML = '';
24564         
24565         while (i < 12) {
24566             var month = {
24567                 tag: 'span',
24568                 cls: 'month',
24569                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24570             };
24571             
24572             months.createChild(month);
24573         }
24574         
24575     },
24576     
24577     update: function()
24578     {
24579         var _this = this;
24580         
24581         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24582             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24583         }
24584         
24585         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24586             e.removeClass('active');
24587             
24588             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24589                 e.addClass('active');
24590             }
24591         })
24592     },
24593     
24594     place: function()
24595     {
24596         if(this.isInline) {
24597             return;
24598         }
24599         
24600         this.picker().removeClass(['bottom', 'top']);
24601         
24602         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24603             /*
24604              * place to the top of element!
24605              *
24606              */
24607             
24608             this.picker().addClass('top');
24609             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24610             
24611             return;
24612         }
24613         
24614         this.picker().addClass('bottom');
24615         
24616         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24617     },
24618     
24619     onFocus : function()
24620     {
24621         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24622         this.show();
24623     },
24624     
24625     onBlur : function()
24626     {
24627         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24628         
24629         var d = this.inputEl().getValue();
24630         
24631         this.setValue(d);
24632                 
24633         this.hide();
24634     },
24635     
24636     show : function()
24637     {
24638         this.picker().show();
24639         this.picker().select('>.datepicker-months', true).first().show();
24640         this.update();
24641         this.place();
24642         
24643         this.fireEvent('show', this, this.date);
24644     },
24645     
24646     hide : function()
24647     {
24648         if(this.isInline) {
24649             return;
24650         }
24651         this.picker().hide();
24652         this.fireEvent('hide', this, this.date);
24653         
24654     },
24655     
24656     onMousedown: function(e)
24657     {
24658         e.stopPropagation();
24659         e.preventDefault();
24660     },
24661     
24662     keyup: function(e)
24663     {
24664         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24665         this.update();
24666     },
24667
24668     fireKey: function(e)
24669     {
24670         if (!this.picker().isVisible()){
24671             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24672                 this.show();
24673             }
24674             return;
24675         }
24676         
24677         var dir;
24678         
24679         switch(e.keyCode){
24680             case 27: // escape
24681                 this.hide();
24682                 e.preventDefault();
24683                 break;
24684             case 37: // left
24685             case 39: // right
24686                 dir = e.keyCode == 37 ? -1 : 1;
24687                 
24688                 this.vIndex = this.vIndex + dir;
24689                 
24690                 if(this.vIndex < 0){
24691                     this.vIndex = 0;
24692                 }
24693                 
24694                 if(this.vIndex > 11){
24695                     this.vIndex = 11;
24696                 }
24697                 
24698                 if(isNaN(this.vIndex)){
24699                     this.vIndex = 0;
24700                 }
24701                 
24702                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24703                 
24704                 break;
24705             case 38: // up
24706             case 40: // down
24707                 
24708                 dir = e.keyCode == 38 ? -1 : 1;
24709                 
24710                 this.vIndex = this.vIndex + dir * 4;
24711                 
24712                 if(this.vIndex < 0){
24713                     this.vIndex = 0;
24714                 }
24715                 
24716                 if(this.vIndex > 11){
24717                     this.vIndex = 11;
24718                 }
24719                 
24720                 if(isNaN(this.vIndex)){
24721                     this.vIndex = 0;
24722                 }
24723                 
24724                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24725                 break;
24726                 
24727             case 13: // enter
24728                 
24729                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24730                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24731                 }
24732                 
24733                 this.hide();
24734                 e.preventDefault();
24735                 break;
24736             case 9: // tab
24737                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24738                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24739                 }
24740                 this.hide();
24741                 break;
24742             case 16: // shift
24743             case 17: // ctrl
24744             case 18: // alt
24745                 break;
24746             default :
24747                 this.hide();
24748                 
24749         }
24750     },
24751     
24752     remove: function() 
24753     {
24754         this.picker().remove();
24755     }
24756    
24757 });
24758
24759 Roo.apply(Roo.bootstrap.form.MonthField,  {
24760     
24761     content : {
24762         tag: 'tbody',
24763         cn: [
24764         {
24765             tag: 'tr',
24766             cn: [
24767             {
24768                 tag: 'td',
24769                 colspan: '7'
24770             }
24771             ]
24772         }
24773         ]
24774     },
24775     
24776     dates:{
24777         en: {
24778             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24779             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24780         }
24781     }
24782 });
24783
24784 Roo.apply(Roo.bootstrap.form.MonthField,  {
24785   
24786     template : {
24787         tag: 'div',
24788         cls: 'datepicker dropdown-menu roo-dynamic',
24789         cn: [
24790             {
24791                 tag: 'div',
24792                 cls: 'datepicker-months',
24793                 cn: [
24794                 {
24795                     tag: 'table',
24796                     cls: 'table-condensed',
24797                     cn:[
24798                         Roo.bootstrap.form.DateField.content
24799                     ]
24800                 }
24801                 ]
24802             }
24803         ]
24804     }
24805 });
24806
24807  
24808
24809  
24810  /*
24811  * - LGPL
24812  *
24813  * CheckBox
24814  * 
24815  */
24816
24817 /**
24818  * @class Roo.bootstrap.form.CheckBox
24819  * @extends Roo.bootstrap.form.Input
24820  * Bootstrap CheckBox class
24821  * 
24822  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24823  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24824  * @cfg {String} boxLabel The text that appears beside the checkbox
24825  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24826  * @cfg {Boolean} checked initnal the element
24827  * @cfg {Boolean} inline inline the element (default false)
24828  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24829  * @cfg {String} tooltip label tooltip
24830  * 
24831  * @constructor
24832  * Create a new CheckBox
24833  * @param {Object} config The config object
24834  */
24835
24836 Roo.bootstrap.form.CheckBox = function(config){
24837     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24838    
24839     this.addEvents({
24840         /**
24841         * @event check
24842         * Fires when the element is checked or unchecked.
24843         * @param {Roo.bootstrap.form.CheckBox} this This input
24844         * @param {Boolean} checked The new checked value
24845         */
24846        check : true,
24847        /**
24848         * @event click
24849         * Fires when the element is click.
24850         * @param {Roo.bootstrap.form.CheckBox} this This input
24851         */
24852        click : true
24853     });
24854     
24855 };
24856
24857 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24858   
24859     inputType: 'checkbox',
24860     inputValue: 1,
24861     valueOff: 0,
24862     boxLabel: false,
24863     checked: false,
24864     weight : false,
24865     inline: false,
24866     tooltip : '',
24867     
24868     // checkbox success does not make any sense really.. 
24869     invalidClass : "",
24870     validClass : "",
24871     
24872     
24873     getAutoCreate : function()
24874     {
24875         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24876         
24877         var id = Roo.id();
24878         
24879         var cfg = {};
24880         
24881         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24882         
24883         if(this.inline){
24884             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24885         }
24886         
24887         var input =  {
24888             tag: 'input',
24889             id : id,
24890             type : this.inputType,
24891             value : this.inputValue,
24892             cls : 'roo-' + this.inputType, //'form-box',
24893             placeholder : this.placeholder || ''
24894             
24895         };
24896         
24897         if(this.inputType != 'radio'){
24898             var hidden =  {
24899                 tag: 'input',
24900                 type : 'hidden',
24901                 cls : 'roo-hidden-value',
24902                 value : this.checked ? this.inputValue : this.valueOff
24903             };
24904         }
24905         
24906             
24907         if (this.weight) { // Validity check?
24908             cfg.cls += " " + this.inputType + "-" + this.weight;
24909         }
24910         
24911         if (this.disabled) {
24912             input.disabled=true;
24913         }
24914         
24915         if(this.checked){
24916             input.checked = this.checked;
24917         }
24918         
24919         if (this.name) {
24920             
24921             input.name = this.name;
24922             
24923             if(this.inputType != 'radio'){
24924                 hidden.name = this.name;
24925                 input.name = '_hidden_' + this.name;
24926             }
24927         }
24928         
24929         if (this.size) {
24930             input.cls += ' input-' + this.size;
24931         }
24932         
24933         var settings=this;
24934         
24935         ['xs','sm','md','lg'].map(function(size){
24936             if (settings[size]) {
24937                 cfg.cls += ' col-' + size + '-' + settings[size];
24938             }
24939         });
24940         
24941         var inputblock = input;
24942          
24943         if (this.before || this.after) {
24944             
24945             inputblock = {
24946                 cls : 'input-group',
24947                 cn :  [] 
24948             };
24949             
24950             if (this.before) {
24951                 inputblock.cn.push({
24952                     tag :'span',
24953                     cls : 'input-group-addon',
24954                     html : this.before
24955                 });
24956             }
24957             
24958             inputblock.cn.push(input);
24959             
24960             if(this.inputType != 'radio'){
24961                 inputblock.cn.push(hidden);
24962             }
24963             
24964             if (this.after) {
24965                 inputblock.cn.push({
24966                     tag :'span',
24967                     cls : 'input-group-addon',
24968                     html : this.after
24969                 });
24970             }
24971             
24972         }
24973         var boxLabelCfg = false;
24974         
24975         if(this.boxLabel){
24976            
24977             boxLabelCfg = {
24978                 tag: 'label',
24979                 //'for': id, // box label is handled by onclick - so no for...
24980                 cls: 'box-label',
24981                 html: this.boxLabel
24982             };
24983             if(this.tooltip){
24984                 boxLabelCfg.tooltip = this.tooltip;
24985             }
24986              
24987         }
24988         
24989         
24990         if (align ==='left' && this.fieldLabel.length) {
24991 //                Roo.log("left and has label");
24992             cfg.cn = [
24993                 {
24994                     tag: 'label',
24995                     'for' :  id,
24996                     cls : 'control-label',
24997                     html : this.fieldLabel
24998                 },
24999                 {
25000                     cls : "", 
25001                     cn: [
25002                         inputblock
25003                     ]
25004                 }
25005             ];
25006             
25007             if (boxLabelCfg) {
25008                 cfg.cn[1].cn.push(boxLabelCfg);
25009             }
25010             
25011             if(this.labelWidth > 12){
25012                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25013             }
25014             
25015             if(this.labelWidth < 13 && this.labelmd == 0){
25016                 this.labelmd = this.labelWidth;
25017             }
25018             
25019             if(this.labellg > 0){
25020                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25021                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25022             }
25023             
25024             if(this.labelmd > 0){
25025                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25026                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25027             }
25028             
25029             if(this.labelsm > 0){
25030                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25031                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25032             }
25033             
25034             if(this.labelxs > 0){
25035                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25036                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25037             }
25038             
25039         } else if ( this.fieldLabel.length) {
25040 //                Roo.log(" label");
25041                 cfg.cn = [
25042                    
25043                     {
25044                         tag: this.boxLabel ? 'span' : 'label',
25045                         'for': id,
25046                         cls: 'control-label box-input-label',
25047                         //cls : 'input-group-addon',
25048                         html : this.fieldLabel
25049                     },
25050                     
25051                     inputblock
25052                     
25053                 ];
25054                 if (boxLabelCfg) {
25055                     cfg.cn.push(boxLabelCfg);
25056                 }
25057
25058         } else {
25059             
25060 //                Roo.log(" no label && no align");
25061                 cfg.cn = [  inputblock ] ;
25062                 if (boxLabelCfg) {
25063                     cfg.cn.push(boxLabelCfg);
25064                 }
25065
25066                 
25067         }
25068         
25069        
25070         
25071         if(this.inputType != 'radio'){
25072             cfg.cn.push(hidden);
25073         }
25074         
25075         return cfg;
25076         
25077     },
25078     
25079     /**
25080      * return the real input element.
25081      */
25082     inputEl: function ()
25083     {
25084         return this.el.select('input.roo-' + this.inputType,true).first();
25085     },
25086     hiddenEl: function ()
25087     {
25088         return this.el.select('input.roo-hidden-value',true).first();
25089     },
25090     
25091     labelEl: function()
25092     {
25093         return this.el.select('label.control-label',true).first();
25094     },
25095     /* depricated... */
25096     
25097     label: function()
25098     {
25099         return this.labelEl();
25100     },
25101     
25102     boxLabelEl: function()
25103     {
25104         return this.el.select('label.box-label',true).first();
25105     },
25106     
25107     initEvents : function()
25108     {
25109 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25110         
25111         this.inputEl().on('click', this.onClick,  this);
25112         
25113         if (this.boxLabel) { 
25114             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25115         }
25116         
25117         this.startValue = this.getValue();
25118         
25119         if(this.groupId){
25120             Roo.bootstrap.form.CheckBox.register(this);
25121         }
25122     },
25123     
25124     onClick : function(e)
25125     {   
25126         if(this.fireEvent('click', this, e) !== false){
25127             this.setChecked(!this.checked);
25128         }
25129         
25130     },
25131     
25132     setChecked : function(state,suppressEvent)
25133     {
25134         this.startValue = this.getValue();
25135
25136         if(this.inputType == 'radio'){
25137             
25138             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25139                 e.dom.checked = false;
25140             });
25141             
25142             this.inputEl().dom.checked = true;
25143             
25144             this.inputEl().dom.value = this.inputValue;
25145             
25146             if(suppressEvent !== true){
25147                 this.fireEvent('check', this, true);
25148             }
25149             
25150             this.validate();
25151             
25152             return;
25153         }
25154         
25155         this.checked = state;
25156         
25157         this.inputEl().dom.checked = state;
25158         
25159         
25160         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25161         
25162         if(suppressEvent !== true){
25163             this.fireEvent('check', this, state);
25164         }
25165         
25166         this.validate();
25167     },
25168     
25169     getValue : function()
25170     {
25171         if(this.inputType == 'radio'){
25172             return this.getGroupValue();
25173         }
25174         
25175         return this.hiddenEl().dom.value;
25176         
25177     },
25178     
25179     getGroupValue : function()
25180     {
25181         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25182             return '';
25183         }
25184         
25185         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25186     },
25187     
25188     setValue : function(v,suppressEvent)
25189     {
25190         if(this.inputType == 'radio'){
25191             this.setGroupValue(v, suppressEvent);
25192             return;
25193         }
25194         
25195         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25196         
25197         this.validate();
25198     },
25199     
25200     setGroupValue : function(v, suppressEvent)
25201     {
25202         this.startValue = this.getValue();
25203         
25204         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25205             e.dom.checked = false;
25206             
25207             if(e.dom.value == v){
25208                 e.dom.checked = true;
25209             }
25210         });
25211         
25212         if(suppressEvent !== true){
25213             this.fireEvent('check', this, true);
25214         }
25215
25216         this.validate();
25217         
25218         return;
25219     },
25220     
25221     validate : function()
25222     {
25223         if(this.getVisibilityEl().hasClass('hidden')){
25224             return true;
25225         }
25226         
25227         if(
25228                 this.disabled || 
25229                 (this.inputType == 'radio' && this.validateRadio()) ||
25230                 (this.inputType == 'checkbox' && this.validateCheckbox())
25231         ){
25232             this.markValid();
25233             return true;
25234         }
25235         
25236         this.markInvalid();
25237         return false;
25238     },
25239     
25240     validateRadio : function()
25241     {
25242         if(this.getVisibilityEl().hasClass('hidden')){
25243             return true;
25244         }
25245         
25246         if(this.allowBlank){
25247             return true;
25248         }
25249         
25250         var valid = false;
25251         
25252         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25253             if(!e.dom.checked){
25254                 return;
25255             }
25256             
25257             valid = true;
25258             
25259             return false;
25260         });
25261         
25262         return valid;
25263     },
25264     
25265     validateCheckbox : function()
25266     {
25267         if(!this.groupId){
25268             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25269             //return (this.getValue() == this.inputValue) ? true : false;
25270         }
25271         
25272         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25273         
25274         if(!group){
25275             return false;
25276         }
25277         
25278         var r = false;
25279         
25280         for(var i in group){
25281             if(group[i].el.isVisible(true)){
25282                 r = false;
25283                 break;
25284             }
25285             
25286             r = true;
25287         }
25288         
25289         for(var i in group){
25290             if(r){
25291                 break;
25292             }
25293             
25294             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25295         }
25296         
25297         return r;
25298     },
25299     
25300     /**
25301      * Mark this field as valid
25302      */
25303     markValid : function()
25304     {
25305         var _this = this;
25306         
25307         this.fireEvent('valid', this);
25308         
25309         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25310         
25311         if(this.groupId){
25312             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25313         }
25314         
25315         if(label){
25316             label.markValid();
25317         }
25318
25319         if(this.inputType == 'radio'){
25320             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25321                 var fg = e.findParent('.form-group', false, true);
25322                 if (Roo.bootstrap.version == 3) {
25323                     fg.removeClass([_this.invalidClass, _this.validClass]);
25324                     fg.addClass(_this.validClass);
25325                 } else {
25326                     fg.removeClass(['is-valid', 'is-invalid']);
25327                     fg.addClass('is-valid');
25328                 }
25329             });
25330             
25331             return;
25332         }
25333
25334         if(!this.groupId){
25335             var fg = this.el.findParent('.form-group', false, true);
25336             if (Roo.bootstrap.version == 3) {
25337                 fg.removeClass([this.invalidClass, this.validClass]);
25338                 fg.addClass(this.validClass);
25339             } else {
25340                 fg.removeClass(['is-valid', 'is-invalid']);
25341                 fg.addClass('is-valid');
25342             }
25343             return;
25344         }
25345         
25346         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25347         
25348         if(!group){
25349             return;
25350         }
25351         
25352         for(var i in group){
25353             var fg = group[i].el.findParent('.form-group', false, true);
25354             if (Roo.bootstrap.version == 3) {
25355                 fg.removeClass([this.invalidClass, this.validClass]);
25356                 fg.addClass(this.validClass);
25357             } else {
25358                 fg.removeClass(['is-valid', 'is-invalid']);
25359                 fg.addClass('is-valid');
25360             }
25361         }
25362     },
25363     
25364      /**
25365      * Mark this field as invalid
25366      * @param {String} msg The validation message
25367      */
25368     markInvalid : function(msg)
25369     {
25370         if(this.allowBlank){
25371             return;
25372         }
25373         
25374         var _this = this;
25375         
25376         this.fireEvent('invalid', this, msg);
25377         
25378         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25379         
25380         if(this.groupId){
25381             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25382         }
25383         
25384         if(label){
25385             label.markInvalid();
25386         }
25387             
25388         if(this.inputType == 'radio'){
25389             
25390             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25391                 var fg = e.findParent('.form-group', false, true);
25392                 if (Roo.bootstrap.version == 3) {
25393                     fg.removeClass([_this.invalidClass, _this.validClass]);
25394                     fg.addClass(_this.invalidClass);
25395                 } else {
25396                     fg.removeClass(['is-invalid', 'is-valid']);
25397                     fg.addClass('is-invalid');
25398                 }
25399             });
25400             
25401             return;
25402         }
25403         
25404         if(!this.groupId){
25405             var fg = this.el.findParent('.form-group', false, true);
25406             if (Roo.bootstrap.version == 3) {
25407                 fg.removeClass([_this.invalidClass, _this.validClass]);
25408                 fg.addClass(_this.invalidClass);
25409             } else {
25410                 fg.removeClass(['is-invalid', 'is-valid']);
25411                 fg.addClass('is-invalid');
25412             }
25413             return;
25414         }
25415         
25416         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25417         
25418         if(!group){
25419             return;
25420         }
25421         
25422         for(var i in group){
25423             var fg = group[i].el.findParent('.form-group', false, true);
25424             if (Roo.bootstrap.version == 3) {
25425                 fg.removeClass([_this.invalidClass, _this.validClass]);
25426                 fg.addClass(_this.invalidClass);
25427             } else {
25428                 fg.removeClass(['is-invalid', 'is-valid']);
25429                 fg.addClass('is-invalid');
25430             }
25431         }
25432         
25433     },
25434     
25435     clearInvalid : function()
25436     {
25437         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25438         
25439         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25440         
25441         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25442         
25443         if (label && label.iconEl) {
25444             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25445             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25446         }
25447     },
25448     
25449     disable : function()
25450     {
25451         if(this.inputType != 'radio'){
25452             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25453             return;
25454         }
25455         
25456         var _this = this;
25457         
25458         if(this.rendered){
25459             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25460                 _this.getActionEl().addClass(this.disabledClass);
25461                 e.dom.disabled = true;
25462             });
25463         }
25464         
25465         this.disabled = true;
25466         this.fireEvent("disable", this);
25467         return this;
25468     },
25469
25470     enable : function()
25471     {
25472         if(this.inputType != 'radio'){
25473             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25474             return;
25475         }
25476         
25477         var _this = this;
25478         
25479         if(this.rendered){
25480             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25481                 _this.getActionEl().removeClass(this.disabledClass);
25482                 e.dom.disabled = false;
25483             });
25484         }
25485         
25486         this.disabled = false;
25487         this.fireEvent("enable", this);
25488         return this;
25489     },
25490     
25491     setBoxLabel : function(v)
25492     {
25493         this.boxLabel = v;
25494         
25495         if(this.rendered){
25496             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25497         }
25498     }
25499
25500 });
25501
25502 Roo.apply(Roo.bootstrap.form.CheckBox, {
25503     
25504     groups: {},
25505     
25506      /**
25507     * register a CheckBox Group
25508     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25509     */
25510     register : function(checkbox)
25511     {
25512         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25513             this.groups[checkbox.groupId] = {};
25514         }
25515         
25516         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25517             return;
25518         }
25519         
25520         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25521         
25522     },
25523     /**
25524     * fetch a CheckBox Group based on the group ID
25525     * @param {string} the group ID
25526     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25527     */
25528     get: function(groupId) {
25529         if (typeof(this.groups[groupId]) == 'undefined') {
25530             return false;
25531         }
25532         
25533         return this.groups[groupId] ;
25534     }
25535     
25536     
25537 });
25538 /*
25539  * - LGPL
25540  *
25541  * RadioItem
25542  * 
25543  */
25544
25545 /**
25546  * @class Roo.bootstrap.form.Radio
25547  * @extends Roo.bootstrap.Component
25548  * Bootstrap Radio class
25549  * @cfg {String} boxLabel - the label associated
25550  * @cfg {String} value - the value of radio
25551  * 
25552  * @constructor
25553  * Create a new Radio
25554  * @param {Object} config The config object
25555  */
25556 Roo.bootstrap.form.Radio = function(config){
25557     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25558     
25559 };
25560
25561 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25562     
25563     boxLabel : '',
25564     
25565     value : '',
25566     
25567     getAutoCreate : function()
25568     {
25569         var cfg = {
25570             tag : 'div',
25571             cls : 'form-group radio',
25572             cn : [
25573                 {
25574                     tag : 'label',
25575                     cls : 'box-label',
25576                     html : this.boxLabel
25577                 }
25578             ]
25579         };
25580         
25581         return cfg;
25582     },
25583     
25584     initEvents : function() 
25585     {
25586         this.parent().register(this);
25587         
25588         this.el.on('click', this.onClick, this);
25589         
25590     },
25591     
25592     onClick : function(e)
25593     {
25594         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25595             this.setChecked(true);
25596         }
25597     },
25598     
25599     setChecked : function(state, suppressEvent)
25600     {
25601         this.parent().setValue(this.value, suppressEvent);
25602         
25603     },
25604     
25605     setBoxLabel : function(v)
25606     {
25607         this.boxLabel = v;
25608         
25609         if(this.rendered){
25610             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25611         }
25612     }
25613     
25614 });
25615  
25616
25617  /*
25618  * - LGPL
25619  *
25620  * Input
25621  * 
25622  */
25623
25624 /**
25625  * @class Roo.bootstrap.form.SecurePass
25626  * @extends Roo.bootstrap.form.Input
25627  * Bootstrap SecurePass class
25628  *
25629  * 
25630  * @constructor
25631  * Create a new SecurePass
25632  * @param {Object} config The config object
25633  */
25634  
25635 Roo.bootstrap.form.SecurePass = function (config) {
25636     // these go here, so the translation tool can replace them..
25637     this.errors = {
25638         PwdEmpty: "Please type a password, and then retype it to confirm.",
25639         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25640         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25641         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25642         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25643         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25644         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25645         TooWeak: "Your password is Too Weak."
25646     },
25647     this.meterLabel = "Password strength:";
25648     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25649     this.meterClass = [
25650         "roo-password-meter-tooweak", 
25651         "roo-password-meter-weak", 
25652         "roo-password-meter-medium", 
25653         "roo-password-meter-strong", 
25654         "roo-password-meter-grey"
25655     ];
25656     
25657     this.errors = {};
25658     
25659     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25660 }
25661
25662 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25663     /**
25664      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25665      * {
25666      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25667      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25668      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25669      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25670      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25671      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25672      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25673      * })
25674      */
25675     // private
25676     
25677     meterWidth: 300,
25678     errorMsg :'',    
25679     errors: false,
25680     imageRoot: '/',
25681     /**
25682      * @cfg {String/Object} Label for the strength meter (defaults to
25683      * 'Password strength:')
25684      */
25685     // private
25686     meterLabel: '',
25687     /**
25688      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25689      * ['Weak', 'Medium', 'Strong'])
25690      */
25691     // private    
25692     pwdStrengths: false,    
25693     // private
25694     strength: 0,
25695     // private
25696     _lastPwd: null,
25697     // private
25698     kCapitalLetter: 0,
25699     kSmallLetter: 1,
25700     kDigit: 2,
25701     kPunctuation: 3,
25702     
25703     insecure: false,
25704     // private
25705     initEvents: function ()
25706     {
25707         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25708
25709         if (this.el.is('input[type=password]') && Roo.isSafari) {
25710             this.el.on('keydown', this.SafariOnKeyDown, this);
25711         }
25712
25713         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25714     },
25715     // private
25716     onRender: function (ct, position)
25717     {
25718         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25719         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25720         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25721
25722         this.trigger.createChild({
25723                    cn: [
25724                     {
25725                     //id: 'PwdMeter',
25726                     tag: 'div',
25727                     cls: 'roo-password-meter-grey col-xs-12',
25728                     style: {
25729                         //width: 0,
25730                         //width: this.meterWidth + 'px'                                                
25731                         }
25732                     },
25733                     {                            
25734                          cls: 'roo-password-meter-text'                          
25735                     }
25736                 ]            
25737         });
25738
25739          
25740         if (this.hideTrigger) {
25741             this.trigger.setDisplayed(false);
25742         }
25743         this.setSize(this.width || '', this.height || '');
25744     },
25745     // private
25746     onDestroy: function ()
25747     {
25748         if (this.trigger) {
25749             this.trigger.removeAllListeners();
25750             this.trigger.remove();
25751         }
25752         if (this.wrap) {
25753             this.wrap.remove();
25754         }
25755         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25756     },
25757     // private
25758     checkStrength: function ()
25759     {
25760         var pwd = this.inputEl().getValue();
25761         if (pwd == this._lastPwd) {
25762             return;
25763         }
25764
25765         var strength;
25766         if (this.ClientSideStrongPassword(pwd)) {
25767             strength = 3;
25768         } else if (this.ClientSideMediumPassword(pwd)) {
25769             strength = 2;
25770         } else if (this.ClientSideWeakPassword(pwd)) {
25771             strength = 1;
25772         } else {
25773             strength = 0;
25774         }
25775         
25776         Roo.log('strength1: ' + strength);
25777         
25778         //var pm = this.trigger.child('div/div/div').dom;
25779         var pm = this.trigger.child('div/div');
25780         pm.removeClass(this.meterClass);
25781         pm.addClass(this.meterClass[strength]);
25782                 
25783         
25784         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25785                 
25786         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25787         
25788         this._lastPwd = pwd;
25789     },
25790     reset: function ()
25791     {
25792         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25793         
25794         this._lastPwd = '';
25795         
25796         var pm = this.trigger.child('div/div');
25797         pm.removeClass(this.meterClass);
25798         pm.addClass('roo-password-meter-grey');        
25799         
25800         
25801         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25802         
25803         pt.innerHTML = '';
25804         this.inputEl().dom.type='password';
25805     },
25806     // private
25807     validateValue: function (value)
25808     {
25809         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25810             return false;
25811         }
25812         if (value.length == 0) {
25813             if (this.allowBlank) {
25814                 this.clearInvalid();
25815                 return true;
25816             }
25817
25818             this.markInvalid(this.errors.PwdEmpty);
25819             this.errorMsg = this.errors.PwdEmpty;
25820             return false;
25821         }
25822         
25823         if(this.insecure){
25824             return true;
25825         }
25826         
25827         if (!value.match(/[\x21-\x7e]+/)) {
25828             this.markInvalid(this.errors.PwdBadChar);
25829             this.errorMsg = this.errors.PwdBadChar;
25830             return false;
25831         }
25832         if (value.length < 6) {
25833             this.markInvalid(this.errors.PwdShort);
25834             this.errorMsg = this.errors.PwdShort;
25835             return false;
25836         }
25837         if (value.length > 16) {
25838             this.markInvalid(this.errors.PwdLong);
25839             this.errorMsg = this.errors.PwdLong;
25840             return false;
25841         }
25842         var strength;
25843         if (this.ClientSideStrongPassword(value)) {
25844             strength = 3;
25845         } else if (this.ClientSideMediumPassword(value)) {
25846             strength = 2;
25847         } else if (this.ClientSideWeakPassword(value)) {
25848             strength = 1;
25849         } else {
25850             strength = 0;
25851         }
25852
25853         
25854         if (strength < 2) {
25855             //this.markInvalid(this.errors.TooWeak);
25856             this.errorMsg = this.errors.TooWeak;
25857             //return false;
25858         }
25859         
25860         
25861         console.log('strength2: ' + strength);
25862         
25863         //var pm = this.trigger.child('div/div/div').dom;
25864         
25865         var pm = this.trigger.child('div/div');
25866         pm.removeClass(this.meterClass);
25867         pm.addClass(this.meterClass[strength]);
25868                 
25869         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25870                 
25871         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25872         
25873         this.errorMsg = ''; 
25874         return true;
25875     },
25876     // private
25877     CharacterSetChecks: function (type)
25878     {
25879         this.type = type;
25880         this.fResult = false;
25881     },
25882     // private
25883     isctype: function (character, type)
25884     {
25885         switch (type) {  
25886             case this.kCapitalLetter:
25887                 if (character >= 'A' && character <= 'Z') {
25888                     return true;
25889                 }
25890                 break;
25891             
25892             case this.kSmallLetter:
25893                 if (character >= 'a' && character <= 'z') {
25894                     return true;
25895                 }
25896                 break;
25897             
25898             case this.kDigit:
25899                 if (character >= '0' && character <= '9') {
25900                     return true;
25901                 }
25902                 break;
25903             
25904             case this.kPunctuation:
25905                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25906                     return true;
25907                 }
25908                 break;
25909             
25910             default:
25911                 return false;
25912         }
25913
25914     },
25915     // private
25916     IsLongEnough: function (pwd, size)
25917     {
25918         return !(pwd == null || isNaN(size) || pwd.length < size);
25919     },
25920     // private
25921     SpansEnoughCharacterSets: function (word, nb)
25922     {
25923         if (!this.IsLongEnough(word, nb))
25924         {
25925             return false;
25926         }
25927
25928         var characterSetChecks = new Array(
25929             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25930             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25931         );
25932         
25933         for (var index = 0; index < word.length; ++index) {
25934             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25935                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25936                     characterSetChecks[nCharSet].fResult = true;
25937                     break;
25938                 }
25939             }
25940         }
25941
25942         var nCharSets = 0;
25943         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25944             if (characterSetChecks[nCharSet].fResult) {
25945                 ++nCharSets;
25946             }
25947         }
25948
25949         if (nCharSets < nb) {
25950             return false;
25951         }
25952         return true;
25953     },
25954     // private
25955     ClientSideStrongPassword: function (pwd)
25956     {
25957         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25958     },
25959     // private
25960     ClientSideMediumPassword: function (pwd)
25961     {
25962         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25963     },
25964     // private
25965     ClientSideWeakPassword: function (pwd)
25966     {
25967         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25968     }
25969           
25970 })//<script type="text/javascript">
25971
25972 /*
25973  * Based  Ext JS Library 1.1.1
25974  * Copyright(c) 2006-2007, Ext JS, LLC.
25975  * LGPL
25976  *
25977  */
25978  
25979 /**
25980  * @class Roo.HtmlEditorCore
25981  * @extends Roo.Component
25982  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25983  *
25984  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25985  */
25986
25987 Roo.HtmlEditorCore = function(config){
25988     
25989     
25990     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25991     
25992     
25993     this.addEvents({
25994         /**
25995          * @event initialize
25996          * Fires when the editor is fully initialized (including the iframe)
25997          * @param {Roo.HtmlEditorCore} this
25998          */
25999         initialize: true,
26000         /**
26001          * @event activate
26002          * Fires when the editor is first receives the focus. Any insertion must wait
26003          * until after this event.
26004          * @param {Roo.HtmlEditorCore} this
26005          */
26006         activate: true,
26007          /**
26008          * @event beforesync
26009          * Fires before the textarea is updated with content from the editor iframe. Return false
26010          * to cancel the sync.
26011          * @param {Roo.HtmlEditorCore} this
26012          * @param {String} html
26013          */
26014         beforesync: true,
26015          /**
26016          * @event beforepush
26017          * Fires before the iframe editor is updated with content from the textarea. Return false
26018          * to cancel the push.
26019          * @param {Roo.HtmlEditorCore} this
26020          * @param {String} html
26021          */
26022         beforepush: true,
26023          /**
26024          * @event sync
26025          * Fires when the textarea is updated with content from the editor iframe.
26026          * @param {Roo.HtmlEditorCore} this
26027          * @param {String} html
26028          */
26029         sync: true,
26030          /**
26031          * @event push
26032          * Fires when the iframe editor is updated with content from the textarea.
26033          * @param {Roo.HtmlEditorCore} this
26034          * @param {String} html
26035          */
26036         push: true,
26037         
26038         /**
26039          * @event editorevent
26040          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26041          * @param {Roo.HtmlEditorCore} this
26042          */
26043         editorevent: true
26044         
26045     });
26046     
26047     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26048     
26049     // defaults : white / black...
26050     this.applyBlacklists();
26051     
26052     
26053     
26054 };
26055
26056
26057 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26058
26059
26060      /**
26061      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26062      */
26063     
26064     owner : false,
26065     
26066      /**
26067      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26068      *                        Roo.resizable.
26069      */
26070     resizable : false,
26071      /**
26072      * @cfg {Number} height (in pixels)
26073      */   
26074     height: 300,
26075    /**
26076      * @cfg {Number} width (in pixels)
26077      */   
26078     width: 500,
26079     
26080     /**
26081      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26082      * 
26083      */
26084     stylesheets: false,
26085     
26086     /**
26087      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26088      */
26089     allowComments: false,
26090     // id of frame..
26091     frameId: false,
26092     
26093     // private properties
26094     validationEvent : false,
26095     deferHeight: true,
26096     initialized : false,
26097     activated : false,
26098     sourceEditMode : false,
26099     onFocus : Roo.emptyFn,
26100     iframePad:3,
26101     hideMode:'offsets',
26102     
26103     clearUp: true,
26104     
26105     // blacklist + whitelisted elements..
26106     black: false,
26107     white: false,
26108      
26109     bodyCls : '',
26110
26111     /**
26112      * Protected method that will not generally be called directly. It
26113      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26114      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26115      */
26116     getDocMarkup : function(){
26117         // body styles..
26118         var st = '';
26119         
26120         // inherit styels from page...?? 
26121         if (this.stylesheets === false) {
26122             
26123             Roo.get(document.head).select('style').each(function(node) {
26124                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26125             });
26126             
26127             Roo.get(document.head).select('link').each(function(node) { 
26128                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26129             });
26130             
26131         } else if (!this.stylesheets.length) {
26132                 // simple..
26133                 st = '<style type="text/css">' +
26134                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26135                    '</style>';
26136         } else {
26137             for (var i in this.stylesheets) { 
26138                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26139             }
26140             
26141         }
26142         
26143         st +=  '<style type="text/css">' +
26144             'IMG { cursor: pointer } ' +
26145         '</style>';
26146
26147         var cls = 'roo-htmleditor-body';
26148         
26149         if(this.bodyCls.length){
26150             cls += ' ' + this.bodyCls;
26151         }
26152         
26153         return '<html><head>' + st  +
26154             //<style type="text/css">' +
26155             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26156             //'</style>' +
26157             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26158     },
26159
26160     // private
26161     onRender : function(ct, position)
26162     {
26163         var _t = this;
26164         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26165         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26166         
26167         
26168         this.el.dom.style.border = '0 none';
26169         this.el.dom.setAttribute('tabIndex', -1);
26170         this.el.addClass('x-hidden hide');
26171         
26172         
26173         
26174         if(Roo.isIE){ // fix IE 1px bogus margin
26175             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26176         }
26177        
26178         
26179         this.frameId = Roo.id();
26180         
26181          
26182         
26183         var iframe = this.owner.wrap.createChild({
26184             tag: 'iframe',
26185             cls: 'form-control', // bootstrap..
26186             id: this.frameId,
26187             name: this.frameId,
26188             frameBorder : 'no',
26189             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26190         }, this.el
26191         );
26192         
26193         
26194         this.iframe = iframe.dom;
26195
26196          this.assignDocWin();
26197         
26198         this.doc.designMode = 'on';
26199        
26200         this.doc.open();
26201         this.doc.write(this.getDocMarkup());
26202         this.doc.close();
26203
26204         
26205         var task = { // must defer to wait for browser to be ready
26206             run : function(){
26207                 //console.log("run task?" + this.doc.readyState);
26208                 this.assignDocWin();
26209                 if(this.doc.body || this.doc.readyState == 'complete'){
26210                     try {
26211                         this.doc.designMode="on";
26212                     } catch (e) {
26213                         return;
26214                     }
26215                     Roo.TaskMgr.stop(task);
26216                     this.initEditor.defer(10, this);
26217                 }
26218             },
26219             interval : 10,
26220             duration: 10000,
26221             scope: this
26222         };
26223         Roo.TaskMgr.start(task);
26224
26225     },
26226
26227     // private
26228     onResize : function(w, h)
26229     {
26230          Roo.log('resize: ' +w + ',' + h );
26231         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26232         if(!this.iframe){
26233             return;
26234         }
26235         if(typeof w == 'number'){
26236             
26237             this.iframe.style.width = w + 'px';
26238         }
26239         if(typeof h == 'number'){
26240             
26241             this.iframe.style.height = h + 'px';
26242             if(this.doc){
26243                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26244             }
26245         }
26246         
26247     },
26248
26249     /**
26250      * Toggles the editor between standard and source edit mode.
26251      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26252      */
26253     toggleSourceEdit : function(sourceEditMode){
26254         
26255         this.sourceEditMode = sourceEditMode === true;
26256         
26257         if(this.sourceEditMode){
26258  
26259             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26260             
26261         }else{
26262             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26263             //this.iframe.className = '';
26264             this.deferFocus();
26265         }
26266         //this.setSize(this.owner.wrap.getSize());
26267         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26268     },
26269
26270     
26271   
26272
26273     /**
26274      * Protected method that will not generally be called directly. If you need/want
26275      * custom HTML cleanup, this is the method you should override.
26276      * @param {String} html The HTML to be cleaned
26277      * return {String} The cleaned HTML
26278      */
26279     cleanHtml : function(html){
26280         html = String(html);
26281         if(html.length > 5){
26282             if(Roo.isSafari){ // strip safari nonsense
26283                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26284             }
26285         }
26286         if(html == '&nbsp;'){
26287             html = '';
26288         }
26289         return html;
26290     },
26291
26292     /**
26293      * HTML Editor -> Textarea
26294      * Protected method that will not generally be called directly. Syncs the contents
26295      * of the editor iframe with the textarea.
26296      */
26297     syncValue : function(){
26298         if(this.initialized){
26299             var bd = (this.doc.body || this.doc.documentElement);
26300             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26301             var html = bd.innerHTML;
26302             if(Roo.isSafari){
26303                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26304                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26305                 if(m && m[1]){
26306                     html = '<div style="'+m[0]+'">' + html + '</div>';
26307                 }
26308             }
26309             html = this.cleanHtml(html);
26310             // fix up the special chars.. normaly like back quotes in word...
26311             // however we do not want to do this with chinese..
26312             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26313                 
26314                 var cc = match.charCodeAt();
26315
26316                 // Get the character value, handling surrogate pairs
26317                 if (match.length == 2) {
26318                     // It's a surrogate pair, calculate the Unicode code point
26319                     var high = match.charCodeAt(0) - 0xD800;
26320                     var low  = match.charCodeAt(1) - 0xDC00;
26321                     cc = (high * 0x400) + low + 0x10000;
26322                 }  else if (
26323                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26324                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26325                     (cc >= 0xf900 && cc < 0xfb00 )
26326                 ) {
26327                         return match;
26328                 }  
26329          
26330                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26331                 return "&#" + cc + ";";
26332                 
26333                 
26334             });
26335             
26336             
26337              
26338             if(this.owner.fireEvent('beforesync', this, html) !== false){
26339                 this.el.dom.value = html;
26340                 this.owner.fireEvent('sync', this, html);
26341             }
26342         }
26343     },
26344
26345     /**
26346      * Protected method that will not generally be called directly. Pushes the value of the textarea
26347      * into the iframe editor.
26348      */
26349     pushValue : function(){
26350         if(this.initialized){
26351             var v = this.el.dom.value.trim();
26352             
26353 //            if(v.length < 1){
26354 //                v = '&#160;';
26355 //            }
26356             
26357             if(this.owner.fireEvent('beforepush', this, v) !== false){
26358                 var d = (this.doc.body || this.doc.documentElement);
26359                 d.innerHTML = v;
26360                 this.cleanUpPaste();
26361                 this.el.dom.value = d.innerHTML;
26362                 this.owner.fireEvent('push', this, v);
26363             }
26364         }
26365     },
26366
26367     // private
26368     deferFocus : function(){
26369         this.focus.defer(10, this);
26370     },
26371
26372     // doc'ed in Field
26373     focus : function(){
26374         if(this.win && !this.sourceEditMode){
26375             this.win.focus();
26376         }else{
26377             this.el.focus();
26378         }
26379     },
26380     
26381     assignDocWin: function()
26382     {
26383         var iframe = this.iframe;
26384         
26385          if(Roo.isIE){
26386             this.doc = iframe.contentWindow.document;
26387             this.win = iframe.contentWindow;
26388         } else {
26389 //            if (!Roo.get(this.frameId)) {
26390 //                return;
26391 //            }
26392 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26393 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26394             
26395             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26396                 return;
26397             }
26398             
26399             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26400             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26401         }
26402     },
26403     
26404     // private
26405     initEditor : function(){
26406         //console.log("INIT EDITOR");
26407         this.assignDocWin();
26408         
26409         
26410         
26411         this.doc.designMode="on";
26412         this.doc.open();
26413         this.doc.write(this.getDocMarkup());
26414         this.doc.close();
26415         
26416         var dbody = (this.doc.body || this.doc.documentElement);
26417         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26418         // this copies styles from the containing element into thsi one..
26419         // not sure why we need all of this..
26420         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26421         
26422         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26423         //ss['background-attachment'] = 'fixed'; // w3c
26424         dbody.bgProperties = 'fixed'; // ie
26425         //Roo.DomHelper.applyStyles(dbody, ss);
26426         Roo.EventManager.on(this.doc, {
26427             //'mousedown': this.onEditorEvent,
26428             'mouseup': this.onEditorEvent,
26429             'dblclick': this.onEditorEvent,
26430             'click': this.onEditorEvent,
26431             'keyup': this.onEditorEvent,
26432             buffer:100,
26433             scope: this
26434         });
26435         if(Roo.isGecko){
26436             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26437         }
26438         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26439             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26440         }
26441         this.initialized = true;
26442
26443         this.owner.fireEvent('initialize', this);
26444         this.pushValue();
26445     },
26446
26447     // private
26448     onDestroy : function(){
26449         
26450         
26451         
26452         if(this.rendered){
26453             
26454             //for (var i =0; i < this.toolbars.length;i++) {
26455             //    // fixme - ask toolbars for heights?
26456             //    this.toolbars[i].onDestroy();
26457            // }
26458             
26459             //this.wrap.dom.innerHTML = '';
26460             //this.wrap.remove();
26461         }
26462     },
26463
26464     // private
26465     onFirstFocus : function(){
26466         
26467         this.assignDocWin();
26468         
26469         
26470         this.activated = true;
26471          
26472     
26473         if(Roo.isGecko){ // prevent silly gecko errors
26474             this.win.focus();
26475             var s = this.win.getSelection();
26476             if(!s.focusNode || s.focusNode.nodeType != 3){
26477                 var r = s.getRangeAt(0);
26478                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26479                 r.collapse(true);
26480                 this.deferFocus();
26481             }
26482             try{
26483                 this.execCmd('useCSS', true);
26484                 this.execCmd('styleWithCSS', false);
26485             }catch(e){}
26486         }
26487         this.owner.fireEvent('activate', this);
26488     },
26489
26490     // private
26491     adjustFont: function(btn){
26492         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26493         //if(Roo.isSafari){ // safari
26494         //    adjust *= 2;
26495        // }
26496         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26497         if(Roo.isSafari){ // safari
26498             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26499             v =  (v < 10) ? 10 : v;
26500             v =  (v > 48) ? 48 : v;
26501             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26502             
26503         }
26504         
26505         
26506         v = Math.max(1, v+adjust);
26507         
26508         this.execCmd('FontSize', v  );
26509     },
26510
26511     onEditorEvent : function(e)
26512     {
26513         this.owner.fireEvent('editorevent', this, e);
26514       //  this.updateToolbar();
26515         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26516     },
26517
26518     insertTag : function(tg)
26519     {
26520         // could be a bit smarter... -> wrap the current selected tRoo..
26521         if (tg.toLowerCase() == 'span' ||
26522             tg.toLowerCase() == 'code' ||
26523             tg.toLowerCase() == 'sup' ||
26524             tg.toLowerCase() == 'sub' 
26525             ) {
26526             
26527             range = this.createRange(this.getSelection());
26528             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26529             wrappingNode.appendChild(range.extractContents());
26530             range.insertNode(wrappingNode);
26531
26532             return;
26533             
26534             
26535             
26536         }
26537         this.execCmd("formatblock",   tg);
26538         
26539     },
26540     
26541     insertText : function(txt)
26542     {
26543         
26544         
26545         var range = this.createRange();
26546         range.deleteContents();
26547                //alert(Sender.getAttribute('label'));
26548                
26549         range.insertNode(this.doc.createTextNode(txt));
26550     } ,
26551     
26552      
26553
26554     /**
26555      * Executes a Midas editor command on the editor document and performs necessary focus and
26556      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26557      * @param {String} cmd The Midas command
26558      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26559      */
26560     relayCmd : function(cmd, value){
26561         this.win.focus();
26562         this.execCmd(cmd, value);
26563         this.owner.fireEvent('editorevent', this);
26564         //this.updateToolbar();
26565         this.owner.deferFocus();
26566     },
26567
26568     /**
26569      * Executes a Midas editor command directly on the editor document.
26570      * For visual commands, you should use {@link #relayCmd} instead.
26571      * <b>This should only be called after the editor is initialized.</b>
26572      * @param {String} cmd The Midas command
26573      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26574      */
26575     execCmd : function(cmd, value){
26576         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26577         this.syncValue();
26578     },
26579  
26580  
26581    
26582     /**
26583      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26584      * to insert tRoo.
26585      * @param {String} text | dom node.. 
26586      */
26587     insertAtCursor : function(text)
26588     {
26589         
26590         if(!this.activated){
26591             return;
26592         }
26593         /*
26594         if(Roo.isIE){
26595             this.win.focus();
26596             var r = this.doc.selection.createRange();
26597             if(r){
26598                 r.collapse(true);
26599                 r.pasteHTML(text);
26600                 this.syncValue();
26601                 this.deferFocus();
26602             
26603             }
26604             return;
26605         }
26606         */
26607         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26608             this.win.focus();
26609             
26610             
26611             // from jquery ui (MIT licenced)
26612             var range, node;
26613             var win = this.win;
26614             
26615             if (win.getSelection && win.getSelection().getRangeAt) {
26616                 range = win.getSelection().getRangeAt(0);
26617                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26618                 range.insertNode(node);
26619             } else if (win.document.selection && win.document.selection.createRange) {
26620                 // no firefox support
26621                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26622                 win.document.selection.createRange().pasteHTML(txt);
26623             } else {
26624                 // no firefox support
26625                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26626                 this.execCmd('InsertHTML', txt);
26627             } 
26628             
26629             this.syncValue();
26630             
26631             this.deferFocus();
26632         }
26633     },
26634  // private
26635     mozKeyPress : function(e){
26636         if(e.ctrlKey){
26637             var c = e.getCharCode(), cmd;
26638           
26639             if(c > 0){
26640                 c = String.fromCharCode(c).toLowerCase();
26641                 switch(c){
26642                     case 'b':
26643                         cmd = 'bold';
26644                         break;
26645                     case 'i':
26646                         cmd = 'italic';
26647                         break;
26648                     
26649                     case 'u':
26650                         cmd = 'underline';
26651                         break;
26652                     
26653                     case 'v':
26654                         this.cleanUpPaste.defer(100, this);
26655                         return;
26656                         
26657                 }
26658                 if(cmd){
26659                     this.win.focus();
26660                     this.execCmd(cmd);
26661                     this.deferFocus();
26662                     e.preventDefault();
26663                 }
26664                 
26665             }
26666         }
26667     },
26668
26669     // private
26670     fixKeys : function(){ // load time branching for fastest keydown performance
26671         if(Roo.isIE){
26672             return function(e){
26673                 var k = e.getKey(), r;
26674                 if(k == e.TAB){
26675                     e.stopEvent();
26676                     r = this.doc.selection.createRange();
26677                     if(r){
26678                         r.collapse(true);
26679                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26680                         this.deferFocus();
26681                     }
26682                     return;
26683                 }
26684                 
26685                 if(k == e.ENTER){
26686                     r = this.doc.selection.createRange();
26687                     if(r){
26688                         var target = r.parentElement();
26689                         if(!target || target.tagName.toLowerCase() != 'li'){
26690                             e.stopEvent();
26691                             r.pasteHTML('<br />');
26692                             r.collapse(false);
26693                             r.select();
26694                         }
26695                     }
26696                 }
26697                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26698                     this.cleanUpPaste.defer(100, this);
26699                     return;
26700                 }
26701                 
26702                 
26703             };
26704         }else if(Roo.isOpera){
26705             return function(e){
26706                 var k = e.getKey();
26707                 if(k == e.TAB){
26708                     e.stopEvent();
26709                     this.win.focus();
26710                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26711                     this.deferFocus();
26712                 }
26713                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26714                     this.cleanUpPaste.defer(100, this);
26715                     return;
26716                 }
26717                 
26718             };
26719         }else if(Roo.isSafari){
26720             return function(e){
26721                 var k = e.getKey();
26722                 
26723                 if(k == e.TAB){
26724                     e.stopEvent();
26725                     this.execCmd('InsertText','\t');
26726                     this.deferFocus();
26727                     return;
26728                 }
26729                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26730                     this.cleanUpPaste.defer(100, this);
26731                     return;
26732                 }
26733                 
26734              };
26735         }
26736     }(),
26737     
26738     getAllAncestors: function()
26739     {
26740         var p = this.getSelectedNode();
26741         var a = [];
26742         if (!p) {
26743             a.push(p); // push blank onto stack..
26744             p = this.getParentElement();
26745         }
26746         
26747         
26748         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26749             a.push(p);
26750             p = p.parentNode;
26751         }
26752         a.push(this.doc.body);
26753         return a;
26754     },
26755     lastSel : false,
26756     lastSelNode : false,
26757     
26758     
26759     getSelection : function() 
26760     {
26761         this.assignDocWin();
26762         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26763     },
26764     
26765     getSelectedNode: function() 
26766     {
26767         // this may only work on Gecko!!!
26768         
26769         // should we cache this!!!!
26770         
26771         
26772         
26773          
26774         var range = this.createRange(this.getSelection()).cloneRange();
26775         
26776         if (Roo.isIE) {
26777             var parent = range.parentElement();
26778             while (true) {
26779                 var testRange = range.duplicate();
26780                 testRange.moveToElementText(parent);
26781                 if (testRange.inRange(range)) {
26782                     break;
26783                 }
26784                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26785                     break;
26786                 }
26787                 parent = parent.parentElement;
26788             }
26789             return parent;
26790         }
26791         
26792         // is ancestor a text element.
26793         var ac =  range.commonAncestorContainer;
26794         if (ac.nodeType == 3) {
26795             ac = ac.parentNode;
26796         }
26797         
26798         var ar = ac.childNodes;
26799          
26800         var nodes = [];
26801         var other_nodes = [];
26802         var has_other_nodes = false;
26803         for (var i=0;i<ar.length;i++) {
26804             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26805                 continue;
26806             }
26807             // fullly contained node.
26808             
26809             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26810                 nodes.push(ar[i]);
26811                 continue;
26812             }
26813             
26814             // probably selected..
26815             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26816                 other_nodes.push(ar[i]);
26817                 continue;
26818             }
26819             // outer..
26820             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26821                 continue;
26822             }
26823             
26824             
26825             has_other_nodes = true;
26826         }
26827         if (!nodes.length && other_nodes.length) {
26828             nodes= other_nodes;
26829         }
26830         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26831             return false;
26832         }
26833         
26834         return nodes[0];
26835     },
26836     createRange: function(sel)
26837     {
26838         // this has strange effects when using with 
26839         // top toolbar - not sure if it's a great idea.
26840         //this.editor.contentWindow.focus();
26841         if (typeof sel != "undefined") {
26842             try {
26843                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26844             } catch(e) {
26845                 return this.doc.createRange();
26846             }
26847         } else {
26848             return this.doc.createRange();
26849         }
26850     },
26851     getParentElement: function()
26852     {
26853         
26854         this.assignDocWin();
26855         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26856         
26857         var range = this.createRange(sel);
26858          
26859         try {
26860             var p = range.commonAncestorContainer;
26861             while (p.nodeType == 3) { // text node
26862                 p = p.parentNode;
26863             }
26864             return p;
26865         } catch (e) {
26866             return null;
26867         }
26868     
26869     },
26870     /***
26871      *
26872      * Range intersection.. the hard stuff...
26873      *  '-1' = before
26874      *  '0' = hits..
26875      *  '1' = after.
26876      *         [ -- selected range --- ]
26877      *   [fail]                        [fail]
26878      *
26879      *    basically..
26880      *      if end is before start or  hits it. fail.
26881      *      if start is after end or hits it fail.
26882      *
26883      *   if either hits (but other is outside. - then it's not 
26884      *   
26885      *    
26886      **/
26887     
26888     
26889     // @see http://www.thismuchiknow.co.uk/?p=64.
26890     rangeIntersectsNode : function(range, node)
26891     {
26892         var nodeRange = node.ownerDocument.createRange();
26893         try {
26894             nodeRange.selectNode(node);
26895         } catch (e) {
26896             nodeRange.selectNodeContents(node);
26897         }
26898     
26899         var rangeStartRange = range.cloneRange();
26900         rangeStartRange.collapse(true);
26901     
26902         var rangeEndRange = range.cloneRange();
26903         rangeEndRange.collapse(false);
26904     
26905         var nodeStartRange = nodeRange.cloneRange();
26906         nodeStartRange.collapse(true);
26907     
26908         var nodeEndRange = nodeRange.cloneRange();
26909         nodeEndRange.collapse(false);
26910     
26911         return rangeStartRange.compareBoundaryPoints(
26912                  Range.START_TO_START, nodeEndRange) == -1 &&
26913                rangeEndRange.compareBoundaryPoints(
26914                  Range.START_TO_START, nodeStartRange) == 1;
26915         
26916          
26917     },
26918     rangeCompareNode : function(range, node)
26919     {
26920         var nodeRange = node.ownerDocument.createRange();
26921         try {
26922             nodeRange.selectNode(node);
26923         } catch (e) {
26924             nodeRange.selectNodeContents(node);
26925         }
26926         
26927         
26928         range.collapse(true);
26929     
26930         nodeRange.collapse(true);
26931      
26932         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26933         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26934          
26935         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26936         
26937         var nodeIsBefore   =  ss == 1;
26938         var nodeIsAfter    = ee == -1;
26939         
26940         if (nodeIsBefore && nodeIsAfter) {
26941             return 0; // outer
26942         }
26943         if (!nodeIsBefore && nodeIsAfter) {
26944             return 1; //right trailed.
26945         }
26946         
26947         if (nodeIsBefore && !nodeIsAfter) {
26948             return 2;  // left trailed.
26949         }
26950         // fully contined.
26951         return 3;
26952     },
26953
26954     // private? - in a new class?
26955     cleanUpPaste :  function()
26956     {
26957         // cleans up the whole document..
26958         Roo.log('cleanuppaste');
26959         
26960         this.cleanUpChildren(this.doc.body);
26961         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26962         if (clean != this.doc.body.innerHTML) {
26963             this.doc.body.innerHTML = clean;
26964         }
26965         
26966     },
26967     
26968     cleanWordChars : function(input) {// change the chars to hex code
26969         var he = Roo.HtmlEditorCore;
26970         
26971         var output = input;
26972         Roo.each(he.swapCodes, function(sw) { 
26973             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26974             
26975             output = output.replace(swapper, sw[1]);
26976         });
26977         
26978         return output;
26979     },
26980     
26981     
26982     cleanUpChildren : function (n)
26983     {
26984         if (!n.childNodes.length) {
26985             return;
26986         }
26987         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26988            this.cleanUpChild(n.childNodes[i]);
26989         }
26990     },
26991     
26992     
26993         
26994     
26995     cleanUpChild : function (node)
26996     {
26997         var ed = this;
26998         //console.log(node);
26999         if (node.nodeName == "#text") {
27000             // clean up silly Windows -- stuff?
27001             return; 
27002         }
27003         if (node.nodeName == "#comment") {
27004             if (!this.allowComments) {
27005                 node.parentNode.removeChild(node);
27006             }
27007             // clean up silly Windows -- stuff?
27008             return; 
27009         }
27010         var lcname = node.tagName.toLowerCase();
27011         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27012         // whitelist of tags..
27013         
27014         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27015             // remove node.
27016             node.parentNode.removeChild(node);
27017             return;
27018             
27019         }
27020         
27021         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27022         
27023         // spans with no attributes - just remove them..
27024         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27025             remove_keep_children = true;
27026         }
27027         
27028         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27029         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27030         
27031         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27032         //    remove_keep_children = true;
27033         //}
27034         
27035         if (remove_keep_children) {
27036             this.cleanUpChildren(node);
27037             // inserts everything just before this node...
27038             while (node.childNodes.length) {
27039                 var cn = node.childNodes[0];
27040                 node.removeChild(cn);
27041                 node.parentNode.insertBefore(cn, node);
27042             }
27043             node.parentNode.removeChild(node);
27044             return;
27045         }
27046         
27047         if (!node.attributes || !node.attributes.length) {
27048             
27049           
27050             
27051             
27052             this.cleanUpChildren(node);
27053             return;
27054         }
27055         
27056         function cleanAttr(n,v)
27057         {
27058             
27059             if (v.match(/^\./) || v.match(/^\//)) {
27060                 return;
27061             }
27062             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27063                 return;
27064             }
27065             if (v.match(/^#/)) {
27066                 return;
27067             }
27068             if (v.match(/^\{/)) { // allow template editing.
27069                 return;
27070             }
27071 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27072             node.removeAttribute(n);
27073             
27074         }
27075         
27076         var cwhite = this.cwhite;
27077         var cblack = this.cblack;
27078             
27079         function cleanStyle(n,v)
27080         {
27081             if (v.match(/expression/)) { //XSS?? should we even bother..
27082                 node.removeAttribute(n);
27083                 return;
27084             }
27085             
27086             var parts = v.split(/;/);
27087             var clean = [];
27088             
27089             Roo.each(parts, function(p) {
27090                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27091                 if (!p.length) {
27092                     return true;
27093                 }
27094                 var l = p.split(':').shift().replace(/\s+/g,'');
27095                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27096                 
27097                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27098 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27099                     //node.removeAttribute(n);
27100                     return true;
27101                 }
27102                 //Roo.log()
27103                 // only allow 'c whitelisted system attributes'
27104                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27105 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27106                     //node.removeAttribute(n);
27107                     return true;
27108                 }
27109                 
27110                 
27111                  
27112                 
27113                 clean.push(p);
27114                 return true;
27115             });
27116             if (clean.length) { 
27117                 node.setAttribute(n, clean.join(';'));
27118             } else {
27119                 node.removeAttribute(n);
27120             }
27121             
27122         }
27123         
27124         
27125         for (var i = node.attributes.length-1; i > -1 ; i--) {
27126             var a = node.attributes[i];
27127             //console.log(a);
27128             
27129             if (a.name.toLowerCase().substr(0,2)=='on')  {
27130                 node.removeAttribute(a.name);
27131                 continue;
27132             }
27133             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27134                 node.removeAttribute(a.name);
27135                 continue;
27136             }
27137             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27138                 cleanAttr(a.name,a.value); // fixme..
27139                 continue;
27140             }
27141             if (a.name == 'style') {
27142                 cleanStyle(a.name,a.value);
27143                 continue;
27144             }
27145             /// clean up MS crap..
27146             // tecnically this should be a list of valid class'es..
27147             
27148             
27149             if (a.name == 'class') {
27150                 if (a.value.match(/^Mso/)) {
27151                     node.removeAttribute('class');
27152                 }
27153                 
27154                 if (a.value.match(/^body$/)) {
27155                     node.removeAttribute('class');
27156                 }
27157                 continue;
27158             }
27159             
27160             // style cleanup!?
27161             // class cleanup?
27162             
27163         }
27164         
27165         
27166         this.cleanUpChildren(node);
27167         
27168         
27169     },
27170     
27171     /**
27172      * Clean up MS wordisms...
27173      */
27174     cleanWord : function(node)
27175     {
27176         if (!node) {
27177             this.cleanWord(this.doc.body);
27178             return;
27179         }
27180         
27181         if(
27182                 node.nodeName == 'SPAN' &&
27183                 !node.hasAttributes() &&
27184                 node.childNodes.length == 1 &&
27185                 node.firstChild.nodeName == "#text"  
27186         ) {
27187             var textNode = node.firstChild;
27188             node.removeChild(textNode);
27189             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27190                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27191             }
27192             node.parentNode.insertBefore(textNode, node);
27193             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27194                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27195             }
27196             node.parentNode.removeChild(node);
27197         }
27198         
27199         if (node.nodeName == "#text") {
27200             // clean up silly Windows -- stuff?
27201             return; 
27202         }
27203         if (node.nodeName == "#comment") {
27204             node.parentNode.removeChild(node);
27205             // clean up silly Windows -- stuff?
27206             return; 
27207         }
27208         
27209         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27210             node.parentNode.removeChild(node);
27211             return;
27212         }
27213         //Roo.log(node.tagName);
27214         // remove - but keep children..
27215         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27216             //Roo.log('-- removed');
27217             while (node.childNodes.length) {
27218                 var cn = node.childNodes[0];
27219                 node.removeChild(cn);
27220                 node.parentNode.insertBefore(cn, node);
27221                 // move node to parent - and clean it..
27222                 this.cleanWord(cn);
27223             }
27224             node.parentNode.removeChild(node);
27225             /// no need to iterate chidlren = it's got none..
27226             //this.iterateChildren(node, this.cleanWord);
27227             return;
27228         }
27229         // clean styles
27230         if (node.className.length) {
27231             
27232             var cn = node.className.split(/\W+/);
27233             var cna = [];
27234             Roo.each(cn, function(cls) {
27235                 if (cls.match(/Mso[a-zA-Z]+/)) {
27236                     return;
27237                 }
27238                 cna.push(cls);
27239             });
27240             node.className = cna.length ? cna.join(' ') : '';
27241             if (!cna.length) {
27242                 node.removeAttribute("class");
27243             }
27244         }
27245         
27246         if (node.hasAttribute("lang")) {
27247             node.removeAttribute("lang");
27248         }
27249         
27250         if (node.hasAttribute("style")) {
27251             
27252             var styles = node.getAttribute("style").split(";");
27253             var nstyle = [];
27254             Roo.each(styles, function(s) {
27255                 if (!s.match(/:/)) {
27256                     return;
27257                 }
27258                 var kv = s.split(":");
27259                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27260                     return;
27261                 }
27262                 // what ever is left... we allow.
27263                 nstyle.push(s);
27264             });
27265             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27266             if (!nstyle.length) {
27267                 node.removeAttribute('style');
27268             }
27269         }
27270         this.iterateChildren(node, this.cleanWord);
27271         
27272         
27273         
27274     },
27275     /**
27276      * iterateChildren of a Node, calling fn each time, using this as the scole..
27277      * @param {DomNode} node node to iterate children of.
27278      * @param {Function} fn method of this class to call on each item.
27279      */
27280     iterateChildren : function(node, fn)
27281     {
27282         if (!node.childNodes.length) {
27283                 return;
27284         }
27285         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27286            fn.call(this, node.childNodes[i])
27287         }
27288     },
27289     
27290     
27291     /**
27292      * cleanTableWidths.
27293      *
27294      * Quite often pasting from word etc.. results in tables with column and widths.
27295      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27296      *
27297      */
27298     cleanTableWidths : function(node)
27299     {
27300          
27301          
27302         if (!node) {
27303             this.cleanTableWidths(this.doc.body);
27304             return;
27305         }
27306         
27307         // ignore list...
27308         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27309             return; 
27310         }
27311         Roo.log(node.tagName);
27312         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27313             this.iterateChildren(node, this.cleanTableWidths);
27314             return;
27315         }
27316         if (node.hasAttribute('width')) {
27317             node.removeAttribute('width');
27318         }
27319         
27320          
27321         if (node.hasAttribute("style")) {
27322             // pretty basic...
27323             
27324             var styles = node.getAttribute("style").split(";");
27325             var nstyle = [];
27326             Roo.each(styles, function(s) {
27327                 if (!s.match(/:/)) {
27328                     return;
27329                 }
27330                 var kv = s.split(":");
27331                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27332                     return;
27333                 }
27334                 // what ever is left... we allow.
27335                 nstyle.push(s);
27336             });
27337             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27338             if (!nstyle.length) {
27339                 node.removeAttribute('style');
27340             }
27341         }
27342         
27343         this.iterateChildren(node, this.cleanTableWidths);
27344         
27345         
27346     },
27347     
27348     
27349     
27350     
27351     domToHTML : function(currentElement, depth, nopadtext) {
27352         
27353         depth = depth || 0;
27354         nopadtext = nopadtext || false;
27355     
27356         if (!currentElement) {
27357             return this.domToHTML(this.doc.body);
27358         }
27359         
27360         //Roo.log(currentElement);
27361         var j;
27362         var allText = false;
27363         var nodeName = currentElement.nodeName;
27364         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27365         
27366         if  (nodeName == '#text') {
27367             
27368             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27369         }
27370         
27371         
27372         var ret = '';
27373         if (nodeName != 'BODY') {
27374              
27375             var i = 0;
27376             // Prints the node tagName, such as <A>, <IMG>, etc
27377             if (tagName) {
27378                 var attr = [];
27379                 for(i = 0; i < currentElement.attributes.length;i++) {
27380                     // quoting?
27381                     var aname = currentElement.attributes.item(i).name;
27382                     if (!currentElement.attributes.item(i).value.length) {
27383                         continue;
27384                     }
27385                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27386                 }
27387                 
27388                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27389             } 
27390             else {
27391                 
27392                 // eack
27393             }
27394         } else {
27395             tagName = false;
27396         }
27397         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27398             return ret;
27399         }
27400         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27401             nopadtext = true;
27402         }
27403         
27404         
27405         // Traverse the tree
27406         i = 0;
27407         var currentElementChild = currentElement.childNodes.item(i);
27408         var allText = true;
27409         var innerHTML  = '';
27410         lastnode = '';
27411         while (currentElementChild) {
27412             // Formatting code (indent the tree so it looks nice on the screen)
27413             var nopad = nopadtext;
27414             if (lastnode == 'SPAN') {
27415                 nopad  = true;
27416             }
27417             // text
27418             if  (currentElementChild.nodeName == '#text') {
27419                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27420                 toadd = nopadtext ? toadd : toadd.trim();
27421                 if (!nopad && toadd.length > 80) {
27422                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27423                 }
27424                 innerHTML  += toadd;
27425                 
27426                 i++;
27427                 currentElementChild = currentElement.childNodes.item(i);
27428                 lastNode = '';
27429                 continue;
27430             }
27431             allText = false;
27432             
27433             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27434                 
27435             // Recursively traverse the tree structure of the child node
27436             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27437             lastnode = currentElementChild.nodeName;
27438             i++;
27439             currentElementChild=currentElement.childNodes.item(i);
27440         }
27441         
27442         ret += innerHTML;
27443         
27444         if (!allText) {
27445                 // The remaining code is mostly for formatting the tree
27446             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27447         }
27448         
27449         
27450         if (tagName) {
27451             ret+= "</"+tagName+">";
27452         }
27453         return ret;
27454         
27455     },
27456         
27457     applyBlacklists : function()
27458     {
27459         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27460         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27461         
27462         this.white = [];
27463         this.black = [];
27464         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27465             if (b.indexOf(tag) > -1) {
27466                 return;
27467             }
27468             this.white.push(tag);
27469             
27470         }, this);
27471         
27472         Roo.each(w, function(tag) {
27473             if (b.indexOf(tag) > -1) {
27474                 return;
27475             }
27476             if (this.white.indexOf(tag) > -1) {
27477                 return;
27478             }
27479             this.white.push(tag);
27480             
27481         }, this);
27482         
27483         
27484         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27485             if (w.indexOf(tag) > -1) {
27486                 return;
27487             }
27488             this.black.push(tag);
27489             
27490         }, this);
27491         
27492         Roo.each(b, function(tag) {
27493             if (w.indexOf(tag) > -1) {
27494                 return;
27495             }
27496             if (this.black.indexOf(tag) > -1) {
27497                 return;
27498             }
27499             this.black.push(tag);
27500             
27501         }, this);
27502         
27503         
27504         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27505         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27506         
27507         this.cwhite = [];
27508         this.cblack = [];
27509         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27510             if (b.indexOf(tag) > -1) {
27511                 return;
27512             }
27513             this.cwhite.push(tag);
27514             
27515         }, this);
27516         
27517         Roo.each(w, function(tag) {
27518             if (b.indexOf(tag) > -1) {
27519                 return;
27520             }
27521             if (this.cwhite.indexOf(tag) > -1) {
27522                 return;
27523             }
27524             this.cwhite.push(tag);
27525             
27526         }, this);
27527         
27528         
27529         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27530             if (w.indexOf(tag) > -1) {
27531                 return;
27532             }
27533             this.cblack.push(tag);
27534             
27535         }, this);
27536         
27537         Roo.each(b, function(tag) {
27538             if (w.indexOf(tag) > -1) {
27539                 return;
27540             }
27541             if (this.cblack.indexOf(tag) > -1) {
27542                 return;
27543             }
27544             this.cblack.push(tag);
27545             
27546         }, this);
27547     },
27548     
27549     setStylesheets : function(stylesheets)
27550     {
27551         if(typeof(stylesheets) == 'string'){
27552             Roo.get(this.iframe.contentDocument.head).createChild({
27553                 tag : 'link',
27554                 rel : 'stylesheet',
27555                 type : 'text/css',
27556                 href : stylesheets
27557             });
27558             
27559             return;
27560         }
27561         var _this = this;
27562      
27563         Roo.each(stylesheets, function(s) {
27564             if(!s.length){
27565                 return;
27566             }
27567             
27568             Roo.get(_this.iframe.contentDocument.head).createChild({
27569                 tag : 'link',
27570                 rel : 'stylesheet',
27571                 type : 'text/css',
27572                 href : s
27573             });
27574         });
27575
27576         
27577     },
27578     
27579     removeStylesheets : function()
27580     {
27581         var _this = this;
27582         
27583         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27584             s.remove();
27585         });
27586     },
27587     
27588     setStyle : function(style)
27589     {
27590         Roo.get(this.iframe.contentDocument.head).createChild({
27591             tag : 'style',
27592             type : 'text/css',
27593             html : style
27594         });
27595
27596         return;
27597     }
27598     
27599     // hide stuff that is not compatible
27600     /**
27601      * @event blur
27602      * @hide
27603      */
27604     /**
27605      * @event change
27606      * @hide
27607      */
27608     /**
27609      * @event focus
27610      * @hide
27611      */
27612     /**
27613      * @event specialkey
27614      * @hide
27615      */
27616     /**
27617      * @cfg {String} fieldClass @hide
27618      */
27619     /**
27620      * @cfg {String} focusClass @hide
27621      */
27622     /**
27623      * @cfg {String} autoCreate @hide
27624      */
27625     /**
27626      * @cfg {String} inputType @hide
27627      */
27628     /**
27629      * @cfg {String} invalidClass @hide
27630      */
27631     /**
27632      * @cfg {String} invalidText @hide
27633      */
27634     /**
27635      * @cfg {String} msgFx @hide
27636      */
27637     /**
27638      * @cfg {String} validateOnBlur @hide
27639      */
27640 });
27641
27642 Roo.HtmlEditorCore.white = [
27643         'area', 'br', 'img', 'input', 'hr', 'wbr',
27644         
27645        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27646        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27647        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27648        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27649        'table',   'ul',         'xmp', 
27650        
27651        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27652       'thead',   'tr', 
27653      
27654       'dir', 'menu', 'ol', 'ul', 'dl',
27655        
27656       'embed',  'object'
27657 ];
27658
27659
27660 Roo.HtmlEditorCore.black = [
27661     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27662         'applet', // 
27663         'base',   'basefont', 'bgsound', 'blink',  'body', 
27664         'frame',  'frameset', 'head',    'html',   'ilayer', 
27665         'iframe', 'layer',  'link',     'meta',    'object',   
27666         'script', 'style' ,'title',  'xml' // clean later..
27667 ];
27668 Roo.HtmlEditorCore.clean = [
27669     'script', 'style', 'title', 'xml'
27670 ];
27671 Roo.HtmlEditorCore.remove = [
27672     'font'
27673 ];
27674 // attributes..
27675
27676 Roo.HtmlEditorCore.ablack = [
27677     'on'
27678 ];
27679     
27680 Roo.HtmlEditorCore.aclean = [ 
27681     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27682 ];
27683
27684 // protocols..
27685 Roo.HtmlEditorCore.pwhite= [
27686         'http',  'https',  'mailto'
27687 ];
27688
27689 // white listed style attributes.
27690 Roo.HtmlEditorCore.cwhite= [
27691       //  'text-align', /// default is to allow most things..
27692       
27693          
27694 //        'font-size'//??
27695 ];
27696
27697 // black listed style attributes.
27698 Roo.HtmlEditorCore.cblack= [
27699       //  'font-size' -- this can be set by the project 
27700 ];
27701
27702
27703 Roo.HtmlEditorCore.swapCodes   =[ 
27704     [    8211, "&#8211;" ], 
27705     [    8212, "&#8212;" ], 
27706     [    8216,  "'" ],  
27707     [    8217, "'" ],  
27708     [    8220, '"' ],  
27709     [    8221, '"' ],  
27710     [    8226, "*" ],  
27711     [    8230, "..." ]
27712 ]; 
27713
27714     /*
27715  * - LGPL
27716  *
27717  * HtmlEditor
27718  * 
27719  */
27720
27721 /**
27722  * @class Roo.bootstrap.form.HtmlEditor
27723  * @extends Roo.bootstrap.form.TextArea
27724  * Bootstrap HtmlEditor class
27725
27726  * @constructor
27727  * Create a new HtmlEditor
27728  * @param {Object} config The config object
27729  */
27730
27731 Roo.bootstrap.form.HtmlEditor = function(config){
27732     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27733     if (!this.toolbars) {
27734         this.toolbars = [];
27735     }
27736     
27737     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27738     this.addEvents({
27739             /**
27740              * @event initialize
27741              * Fires when the editor is fully initialized (including the iframe)
27742              * @param {HtmlEditor} this
27743              */
27744             initialize: true,
27745             /**
27746              * @event activate
27747              * Fires when the editor is first receives the focus. Any insertion must wait
27748              * until after this event.
27749              * @param {HtmlEditor} this
27750              */
27751             activate: true,
27752              /**
27753              * @event beforesync
27754              * Fires before the textarea is updated with content from the editor iframe. Return false
27755              * to cancel the sync.
27756              * @param {HtmlEditor} this
27757              * @param {String} html
27758              */
27759             beforesync: true,
27760              /**
27761              * @event beforepush
27762              * Fires before the iframe editor is updated with content from the textarea. Return false
27763              * to cancel the push.
27764              * @param {HtmlEditor} this
27765              * @param {String} html
27766              */
27767             beforepush: true,
27768              /**
27769              * @event sync
27770              * Fires when the textarea is updated with content from the editor iframe.
27771              * @param {HtmlEditor} this
27772              * @param {String} html
27773              */
27774             sync: true,
27775              /**
27776              * @event push
27777              * Fires when the iframe editor is updated with content from the textarea.
27778              * @param {HtmlEditor} this
27779              * @param {String} html
27780              */
27781             push: true,
27782              /**
27783              * @event editmodechange
27784              * Fires when the editor switches edit modes
27785              * @param {HtmlEditor} this
27786              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27787              */
27788             editmodechange: true,
27789             /**
27790              * @event editorevent
27791              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27792              * @param {HtmlEditor} this
27793              */
27794             editorevent: true,
27795             /**
27796              * @event firstfocus
27797              * Fires when on first focus - needed by toolbars..
27798              * @param {HtmlEditor} this
27799              */
27800             firstfocus: true,
27801             /**
27802              * @event autosave
27803              * Auto save the htmlEditor value as a file into Events
27804              * @param {HtmlEditor} this
27805              */
27806             autosave: true,
27807             /**
27808              * @event savedpreview
27809              * preview the saved version of htmlEditor
27810              * @param {HtmlEditor} this
27811              */
27812             savedpreview: true
27813         });
27814 };
27815
27816
27817 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27818     
27819     
27820       /**
27821      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27822      */
27823     toolbars : false,
27824     
27825      /**
27826     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27827     */
27828     btns : [],
27829    
27830      /**
27831      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27832      *                        Roo.resizable.
27833      */
27834     resizable : false,
27835      /**
27836      * @cfg {Number} height (in pixels)
27837      */   
27838     height: 300,
27839    /**
27840      * @cfg {Number} width (in pixels)
27841      */   
27842     width: false,
27843     
27844     /**
27845      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27846      * 
27847      */
27848     stylesheets: false,
27849     
27850     // id of frame..
27851     frameId: false,
27852     
27853     // private properties
27854     validationEvent : false,
27855     deferHeight: true,
27856     initialized : false,
27857     activated : false,
27858     
27859     onFocus : Roo.emptyFn,
27860     iframePad:3,
27861     hideMode:'offsets',
27862     
27863     tbContainer : false,
27864     
27865     bodyCls : '',
27866     
27867     toolbarContainer :function() {
27868         return this.wrap.select('.x-html-editor-tb',true).first();
27869     },
27870
27871     /**
27872      * Protected method that will not generally be called directly. It
27873      * is called when the editor creates its toolbar. Override this method if you need to
27874      * add custom toolbar buttons.
27875      * @param {HtmlEditor} editor
27876      */
27877     createToolbar : function(){
27878         Roo.log('renewing');
27879         Roo.log("create toolbars");
27880         
27881         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27882         this.toolbars[0].render(this.toolbarContainer());
27883         
27884         return;
27885         
27886 //        if (!editor.toolbars || !editor.toolbars.length) {
27887 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27888 //        }
27889 //        
27890 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27891 //            editor.toolbars[i] = Roo.factory(
27892 //                    typeof(editor.toolbars[i]) == 'string' ?
27893 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27894 //                Roo.bootstrap.form.HtmlEditor);
27895 //            editor.toolbars[i].init(editor);
27896 //        }
27897     },
27898
27899      
27900     // private
27901     onRender : function(ct, position)
27902     {
27903        // Roo.log("Call onRender: " + this.xtype);
27904         var _t = this;
27905         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27906       
27907         this.wrap = this.inputEl().wrap({
27908             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27909         });
27910         
27911         this.editorcore.onRender(ct, position);
27912          
27913         if (this.resizable) {
27914             this.resizeEl = new Roo.Resizable(this.wrap, {
27915                 pinned : true,
27916                 wrap: true,
27917                 dynamic : true,
27918                 minHeight : this.height,
27919                 height: this.height,
27920                 handles : this.resizable,
27921                 width: this.width,
27922                 listeners : {
27923                     resize : function(r, w, h) {
27924                         _t.onResize(w,h); // -something
27925                     }
27926                 }
27927             });
27928             
27929         }
27930         this.createToolbar(this);
27931        
27932         
27933         if(!this.width && this.resizable){
27934             this.setSize(this.wrap.getSize());
27935         }
27936         if (this.resizeEl) {
27937             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27938             // should trigger onReize..
27939         }
27940         
27941     },
27942
27943     // private
27944     onResize : function(w, h)
27945     {
27946         Roo.log('resize: ' +w + ',' + h );
27947         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27948         var ew = false;
27949         var eh = false;
27950         
27951         if(this.inputEl() ){
27952             if(typeof w == 'number'){
27953                 var aw = w - this.wrap.getFrameWidth('lr');
27954                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27955                 ew = aw;
27956             }
27957             if(typeof h == 'number'){
27958                  var tbh = -11;  // fixme it needs to tool bar size!
27959                 for (var i =0; i < this.toolbars.length;i++) {
27960                     // fixme - ask toolbars for heights?
27961                     tbh += this.toolbars[i].el.getHeight();
27962                     //if (this.toolbars[i].footer) {
27963                     //    tbh += this.toolbars[i].footer.el.getHeight();
27964                     //}
27965                 }
27966               
27967                 
27968                 
27969                 
27970                 
27971                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27972                 ah -= 5; // knock a few pixes off for look..
27973                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27974                 var eh = ah;
27975             }
27976         }
27977         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27978         this.editorcore.onResize(ew,eh);
27979         
27980     },
27981
27982     /**
27983      * Toggles the editor between standard and source edit mode.
27984      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27985      */
27986     toggleSourceEdit : function(sourceEditMode)
27987     {
27988         this.editorcore.toggleSourceEdit(sourceEditMode);
27989         
27990         if(this.editorcore.sourceEditMode){
27991             Roo.log('editor - showing textarea');
27992             
27993 //            Roo.log('in');
27994 //            Roo.log(this.syncValue());
27995             this.syncValue();
27996             this.inputEl().removeClass(['hide', 'x-hidden']);
27997             this.inputEl().dom.removeAttribute('tabIndex');
27998             this.inputEl().focus();
27999         }else{
28000             Roo.log('editor - hiding textarea');
28001 //            Roo.log('out')
28002 //            Roo.log(this.pushValue()); 
28003             this.pushValue();
28004             
28005             this.inputEl().addClass(['hide', 'x-hidden']);
28006             this.inputEl().dom.setAttribute('tabIndex', -1);
28007             //this.deferFocus();
28008         }
28009          
28010         if(this.resizable){
28011             this.setSize(this.wrap.getSize());
28012         }
28013         
28014         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28015     },
28016  
28017     // private (for BoxComponent)
28018     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28019
28020     // private (for BoxComponent)
28021     getResizeEl : function(){
28022         return this.wrap;
28023     },
28024
28025     // private (for BoxComponent)
28026     getPositionEl : function(){
28027         return this.wrap;
28028     },
28029
28030     // private
28031     initEvents : function(){
28032         this.originalValue = this.getValue();
28033     },
28034
28035 //    /**
28036 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28037 //     * @method
28038 //     */
28039 //    markInvalid : Roo.emptyFn,
28040 //    /**
28041 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28042 //     * @method
28043 //     */
28044 //    clearInvalid : Roo.emptyFn,
28045
28046     setValue : function(v){
28047         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28048         this.editorcore.pushValue();
28049     },
28050
28051      
28052     // private
28053     deferFocus : function(){
28054         this.focus.defer(10, this);
28055     },
28056
28057     // doc'ed in Field
28058     focus : function(){
28059         this.editorcore.focus();
28060         
28061     },
28062       
28063
28064     // private
28065     onDestroy : function(){
28066         
28067         
28068         
28069         if(this.rendered){
28070             
28071             for (var i =0; i < this.toolbars.length;i++) {
28072                 // fixme - ask toolbars for heights?
28073                 this.toolbars[i].onDestroy();
28074             }
28075             
28076             this.wrap.dom.innerHTML = '';
28077             this.wrap.remove();
28078         }
28079     },
28080
28081     // private
28082     onFirstFocus : function(){
28083         //Roo.log("onFirstFocus");
28084         this.editorcore.onFirstFocus();
28085          for (var i =0; i < this.toolbars.length;i++) {
28086             this.toolbars[i].onFirstFocus();
28087         }
28088         
28089     },
28090     
28091     // private
28092     syncValue : function()
28093     {   
28094         this.editorcore.syncValue();
28095     },
28096     
28097     pushValue : function()
28098     {   
28099         this.editorcore.pushValue();
28100     }
28101      
28102     
28103     // hide stuff that is not compatible
28104     /**
28105      * @event blur
28106      * @hide
28107      */
28108     /**
28109      * @event change
28110      * @hide
28111      */
28112     /**
28113      * @event focus
28114      * @hide
28115      */
28116     /**
28117      * @event specialkey
28118      * @hide
28119      */
28120     /**
28121      * @cfg {String} fieldClass @hide
28122      */
28123     /**
28124      * @cfg {String} focusClass @hide
28125      */
28126     /**
28127      * @cfg {String} autoCreate @hide
28128      */
28129     /**
28130      * @cfg {String} inputType @hide
28131      */
28132      
28133     /**
28134      * @cfg {String} invalidText @hide
28135      */
28136     /**
28137      * @cfg {String} msgFx @hide
28138      */
28139     /**
28140      * @cfg {String} validateOnBlur @hide
28141      */
28142 });
28143  
28144     
28145    
28146    
28147    
28148       
28149 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28150 /**
28151  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28152  * @parent Roo.bootstrap.form.HtmlEditor
28153  * @extends Roo.bootstrap.nav.Simplebar
28154  * Basic Toolbar
28155  * 
28156  * @example
28157  * Usage:
28158  *
28159  new Roo.bootstrap.form.HtmlEditor({
28160     ....
28161     toolbars : [
28162         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28163             disable : { fonts: 1 , format: 1, ..., ... , ...],
28164             btns : [ .... ]
28165         })
28166     }
28167      
28168  * 
28169  * @cfg {Object} disable List of elements to disable..
28170  * @cfg {Array} btns List of additional buttons.
28171  * 
28172  * 
28173  * NEEDS Extra CSS? 
28174  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28175  */
28176  
28177 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28178 {
28179     
28180     Roo.apply(this, config);
28181     
28182     // default disabled, based on 'good practice'..
28183     this.disable = this.disable || {};
28184     Roo.applyIf(this.disable, {
28185         fontSize : true,
28186         colors : true,
28187         specialElements : true
28188     });
28189     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28190     
28191     this.editor = config.editor;
28192     this.editorcore = config.editor.editorcore;
28193     
28194     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28195     
28196     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28197     // dont call parent... till later.
28198 }
28199 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28200      
28201     bar : true,
28202     
28203     editor : false,
28204     editorcore : false,
28205     
28206     
28207     formats : [
28208         "p" ,  
28209         "h1","h2","h3","h4","h5","h6", 
28210         "pre", "code", 
28211         "abbr", "acronym", "address", "cite", "samp", "var",
28212         'div','span'
28213     ],
28214     
28215     onRender : function(ct, position)
28216     {
28217        // Roo.log("Call onRender: " + this.xtype);
28218         
28219        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28220        Roo.log(this.el);
28221        this.el.dom.style.marginBottom = '0';
28222        var _this = this;
28223        var editorcore = this.editorcore;
28224        var editor= this.editor;
28225        
28226        var children = [];
28227        var btn = function(id,cmd , toggle, handler, html){
28228        
28229             var  event = toggle ? 'toggle' : 'click';
28230        
28231             var a = {
28232                 size : 'sm',
28233                 xtype: 'Button',
28234                 xns: Roo.bootstrap,
28235                 //glyphicon : id,
28236                 fa: id,
28237                 cmd : id || cmd,
28238                 enableToggle:toggle !== false,
28239                 html : html || '',
28240                 pressed : toggle ? false : null,
28241                 listeners : {}
28242             };
28243             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28244                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28245             };
28246             children.push(a);
28247             return a;
28248        }
28249        
28250     //    var cb_box = function...
28251         
28252         var style = {
28253                 xtype: 'Button',
28254                 size : 'sm',
28255                 xns: Roo.bootstrap,
28256                 fa : 'font',
28257                 //html : 'submit'
28258                 menu : {
28259                     xtype: 'Menu',
28260                     xns: Roo.bootstrap,
28261                     items:  []
28262                 }
28263         };
28264         Roo.each(this.formats, function(f) {
28265             style.menu.items.push({
28266                 xtype :'MenuItem',
28267                 xns: Roo.bootstrap,
28268                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28269                 tagname : f,
28270                 listeners : {
28271                     click : function()
28272                     {
28273                         editorcore.insertTag(this.tagname);
28274                         editor.focus();
28275                     }
28276                 }
28277                 
28278             });
28279         });
28280         children.push(style);   
28281         
28282         btn('bold',false,true);
28283         btn('italic',false,true);
28284         btn('align-left', 'justifyleft',true);
28285         btn('align-center', 'justifycenter',true);
28286         btn('align-right' , 'justifyright',true);
28287         btn('link', false, false, function(btn) {
28288             //Roo.log("create link?");
28289             var url = prompt(this.createLinkText, this.defaultLinkValue);
28290             if(url && url != 'http:/'+'/'){
28291                 this.editorcore.relayCmd('createlink', url);
28292             }
28293         }),
28294         btn('list','insertunorderedlist',true);
28295         btn('pencil', false,true, function(btn){
28296                 Roo.log(this);
28297                 this.toggleSourceEdit(btn.pressed);
28298         });
28299         
28300         if (this.editor.btns.length > 0) {
28301             for (var i = 0; i<this.editor.btns.length; i++) {
28302                 children.push(this.editor.btns[i]);
28303             }
28304         }
28305         
28306         /*
28307         var cog = {
28308                 xtype: 'Button',
28309                 size : 'sm',
28310                 xns: Roo.bootstrap,
28311                 glyphicon : 'cog',
28312                 //html : 'submit'
28313                 menu : {
28314                     xtype: 'Menu',
28315                     xns: Roo.bootstrap,
28316                     items:  []
28317                 }
28318         };
28319         
28320         cog.menu.items.push({
28321             xtype :'MenuItem',
28322             xns: Roo.bootstrap,
28323             html : Clean styles,
28324             tagname : f,
28325             listeners : {
28326                 click : function()
28327                 {
28328                     editorcore.insertTag(this.tagname);
28329                     editor.focus();
28330                 }
28331             }
28332             
28333         });
28334        */
28335         
28336          
28337        this.xtype = 'NavSimplebar';
28338         
28339         for(var i=0;i< children.length;i++) {
28340             
28341             this.buttons.add(this.addxtypeChild(children[i]));
28342             
28343         }
28344         
28345         editor.on('editorevent', this.updateToolbar, this);
28346     },
28347     onBtnClick : function(id)
28348     {
28349        this.editorcore.relayCmd(id);
28350        this.editorcore.focus();
28351     },
28352     
28353     /**
28354      * Protected method that will not generally be called directly. It triggers
28355      * a toolbar update by reading the markup state of the current selection in the editor.
28356      */
28357     updateToolbar: function(){
28358
28359         if(!this.editorcore.activated){
28360             this.editor.onFirstFocus(); // is this neeed?
28361             return;
28362         }
28363
28364         var btns = this.buttons; 
28365         var doc = this.editorcore.doc;
28366         btns.get('bold').setActive(doc.queryCommandState('bold'));
28367         btns.get('italic').setActive(doc.queryCommandState('italic'));
28368         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28369         
28370         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28371         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28372         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28373         
28374         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28375         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28376          /*
28377         
28378         var ans = this.editorcore.getAllAncestors();
28379         if (this.formatCombo) {
28380             
28381             
28382             var store = this.formatCombo.store;
28383             this.formatCombo.setValue("");
28384             for (var i =0; i < ans.length;i++) {
28385                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28386                     // select it..
28387                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28388                     break;
28389                 }
28390             }
28391         }
28392         
28393         
28394         
28395         // hides menus... - so this cant be on a menu...
28396         Roo.bootstrap.MenuMgr.hideAll();
28397         */
28398         Roo.bootstrap.menu.Manager.hideAll();
28399         //this.editorsyncValue();
28400     },
28401     onFirstFocus: function() {
28402         this.buttons.each(function(item){
28403            item.enable();
28404         });
28405     },
28406     toggleSourceEdit : function(sourceEditMode){
28407         
28408           
28409         if(sourceEditMode){
28410             Roo.log("disabling buttons");
28411            this.buttons.each( function(item){
28412                 if(item.cmd != 'pencil'){
28413                     item.disable();
28414                 }
28415             });
28416           
28417         }else{
28418             Roo.log("enabling buttons");
28419             if(this.editorcore.initialized){
28420                 this.buttons.each( function(item){
28421                     item.enable();
28422                 });
28423             }
28424             
28425         }
28426         Roo.log("calling toggole on editor");
28427         // tell the editor that it's been pressed..
28428         this.editor.toggleSourceEdit(sourceEditMode);
28429        
28430     }
28431 });
28432
28433
28434
28435
28436  
28437 /*
28438  * - LGPL
28439  */
28440
28441 /**
28442  * @class Roo.bootstrap.form.Markdown
28443  * @extends Roo.bootstrap.form.TextArea
28444  * Bootstrap Showdown editable area
28445  * @cfg {string} content
28446  * 
28447  * @constructor
28448  * Create a new Showdown
28449  */
28450
28451 Roo.bootstrap.form.Markdown = function(config){
28452     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28453    
28454 };
28455
28456 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28457     
28458     editing :false,
28459     
28460     initEvents : function()
28461     {
28462         
28463         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28464         this.markdownEl = this.el.createChild({
28465             cls : 'roo-markdown-area'
28466         });
28467         this.inputEl().addClass('d-none');
28468         if (this.getValue() == '') {
28469             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28470             
28471         } else {
28472             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28473         }
28474         this.markdownEl.on('click', this.toggleTextEdit, this);
28475         this.on('blur', this.toggleTextEdit, this);
28476         this.on('specialkey', this.resizeTextArea, this);
28477     },
28478     
28479     toggleTextEdit : function()
28480     {
28481         var sh = this.markdownEl.getHeight();
28482         this.inputEl().addClass('d-none');
28483         this.markdownEl.addClass('d-none');
28484         if (!this.editing) {
28485             // show editor?
28486             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28487             this.inputEl().removeClass('d-none');
28488             this.inputEl().focus();
28489             this.editing = true;
28490             return;
28491         }
28492         // show showdown...
28493         this.updateMarkdown();
28494         this.markdownEl.removeClass('d-none');
28495         this.editing = false;
28496         return;
28497     },
28498     updateMarkdown : function()
28499     {
28500         if (this.getValue() == '') {
28501             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28502             return;
28503         }
28504  
28505         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28506     },
28507     
28508     resizeTextArea: function () {
28509         
28510         var sh = 100;
28511         Roo.log([sh, this.getValue().split("\n").length * 30]);
28512         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28513     },
28514     setValue : function(val)
28515     {
28516         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28517         if (!this.editing) {
28518             this.updateMarkdown();
28519         }
28520         
28521     },
28522     focus : function()
28523     {
28524         if (!this.editing) {
28525             this.toggleTextEdit();
28526         }
28527         
28528     }
28529
28530
28531 });/*
28532  * Based on:
28533  * Ext JS Library 1.1.1
28534  * Copyright(c) 2006-2007, Ext JS, LLC.
28535  *
28536  * Originally Released Under LGPL - original licence link has changed is not relivant.
28537  *
28538  * Fork - LGPL
28539  * <script type="text/javascript">
28540  */
28541  
28542 /**
28543  * @class Roo.bootstrap.PagingToolbar
28544  * @extends Roo.bootstrap.nav.Simplebar
28545  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28546  * @constructor
28547  * Create a new PagingToolbar
28548  * @param {Object} config The config object
28549  * @param {Roo.data.Store} store
28550  */
28551 Roo.bootstrap.PagingToolbar = function(config)
28552 {
28553     // old args format still supported... - xtype is prefered..
28554         // created from xtype...
28555     
28556     this.ds = config.dataSource;
28557     
28558     if (config.store && !this.ds) {
28559         this.store= Roo.factory(config.store, Roo.data);
28560         this.ds = this.store;
28561         this.ds.xmodule = this.xmodule || false;
28562     }
28563     
28564     this.toolbarItems = [];
28565     if (config.items) {
28566         this.toolbarItems = config.items;
28567     }
28568     
28569     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28570     
28571     this.cursor = 0;
28572     
28573     if (this.ds) { 
28574         this.bind(this.ds);
28575     }
28576     
28577     if (Roo.bootstrap.version == 4) {
28578         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28579     } else {
28580         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28581     }
28582     
28583 };
28584
28585 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28586     /**
28587      * @cfg {Roo.bootstrap.Button} buttons[]
28588      * Buttons for the toolbar
28589      */
28590      /**
28591      * @cfg {Roo.data.Store} store
28592      * The underlying data store providing the paged data
28593      */
28594     /**
28595      * @cfg {String/HTMLElement/Element} container
28596      * container The id or element that will contain the toolbar
28597      */
28598     /**
28599      * @cfg {Boolean} displayInfo
28600      * True to display the displayMsg (defaults to false)
28601      */
28602     /**
28603      * @cfg {Number} pageSize
28604      * The number of records to display per page (defaults to 20)
28605      */
28606     pageSize: 20,
28607     /**
28608      * @cfg {String} displayMsg
28609      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28610      */
28611     displayMsg : 'Displaying {0} - {1} of {2}',
28612     /**
28613      * @cfg {String} emptyMsg
28614      * The message to display when no records are found (defaults to "No data to display")
28615      */
28616     emptyMsg : 'No data to display',
28617     /**
28618      * Customizable piece of the default paging text (defaults to "Page")
28619      * @type String
28620      */
28621     beforePageText : "Page",
28622     /**
28623      * Customizable piece of the default paging text (defaults to "of %0")
28624      * @type String
28625      */
28626     afterPageText : "of {0}",
28627     /**
28628      * Customizable piece of the default paging text (defaults to "First Page")
28629      * @type String
28630      */
28631     firstText : "First Page",
28632     /**
28633      * Customizable piece of the default paging text (defaults to "Previous Page")
28634      * @type String
28635      */
28636     prevText : "Previous Page",
28637     /**
28638      * Customizable piece of the default paging text (defaults to "Next Page")
28639      * @type String
28640      */
28641     nextText : "Next Page",
28642     /**
28643      * Customizable piece of the default paging text (defaults to "Last Page")
28644      * @type String
28645      */
28646     lastText : "Last Page",
28647     /**
28648      * Customizable piece of the default paging text (defaults to "Refresh")
28649      * @type String
28650      */
28651     refreshText : "Refresh",
28652
28653     buttons : false,
28654     // private
28655     onRender : function(ct, position) 
28656     {
28657         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28658         this.navgroup.parentId = this.id;
28659         this.navgroup.onRender(this.el, null);
28660         // add the buttons to the navgroup
28661         
28662         if(this.displayInfo){
28663             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28664             this.displayEl = this.el.select('.x-paging-info', true).first();
28665 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28666 //            this.displayEl = navel.el.select('span',true).first();
28667         }
28668         
28669         var _this = this;
28670         
28671         if(this.buttons){
28672             Roo.each(_this.buttons, function(e){ // this might need to use render????
28673                Roo.factory(e).render(_this.el);
28674             });
28675         }
28676             
28677         Roo.each(_this.toolbarItems, function(e) {
28678             _this.navgroup.addItem(e);
28679         });
28680         
28681         
28682         this.first = this.navgroup.addItem({
28683             tooltip: this.firstText,
28684             cls: "prev btn-outline-secondary",
28685             html : ' <i class="fa fa-step-backward"></i>',
28686             disabled: true,
28687             preventDefault: true,
28688             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28689         });
28690         
28691         this.prev =  this.navgroup.addItem({
28692             tooltip: this.prevText,
28693             cls: "prev btn-outline-secondary",
28694             html : ' <i class="fa fa-backward"></i>',
28695             disabled: true,
28696             preventDefault: true,
28697             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28698         });
28699     //this.addSeparator();
28700         
28701         
28702         var field = this.navgroup.addItem( {
28703             tagtype : 'span',
28704             cls : 'x-paging-position  btn-outline-secondary',
28705              disabled: true,
28706             html : this.beforePageText  +
28707                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28708                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28709          } ); //?? escaped?
28710         
28711         this.field = field.el.select('input', true).first();
28712         this.field.on("keydown", this.onPagingKeydown, this);
28713         this.field.on("focus", function(){this.dom.select();});
28714     
28715     
28716         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28717         //this.field.setHeight(18);
28718         //this.addSeparator();
28719         this.next = this.navgroup.addItem({
28720             tooltip: this.nextText,
28721             cls: "next btn-outline-secondary",
28722             html : ' <i class="fa fa-forward"></i>',
28723             disabled: true,
28724             preventDefault: true,
28725             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28726         });
28727         this.last = this.navgroup.addItem({
28728             tooltip: this.lastText,
28729             html : ' <i class="fa fa-step-forward"></i>',
28730             cls: "next btn-outline-secondary",
28731             disabled: true,
28732             preventDefault: true,
28733             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28734         });
28735     //this.addSeparator();
28736         this.loading = this.navgroup.addItem({
28737             tooltip: this.refreshText,
28738             cls: "btn-outline-secondary",
28739             html : ' <i class="fa fa-refresh"></i>',
28740             preventDefault: true,
28741             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28742         });
28743         
28744     },
28745
28746     // private
28747     updateInfo : function(){
28748         if(this.displayEl){
28749             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28750             var msg = count == 0 ?
28751                 this.emptyMsg :
28752                 String.format(
28753                     this.displayMsg,
28754                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28755                 );
28756             this.displayEl.update(msg);
28757         }
28758     },
28759
28760     // private
28761     onLoad : function(ds, r, o)
28762     {
28763         this.cursor = o.params && o.params.start ? o.params.start : 0;
28764         
28765         var d = this.getPageData(),
28766             ap = d.activePage,
28767             ps = d.pages;
28768         
28769         
28770         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28771         this.field.dom.value = ap;
28772         this.first.setDisabled(ap == 1);
28773         this.prev.setDisabled(ap == 1);
28774         this.next.setDisabled(ap == ps);
28775         this.last.setDisabled(ap == ps);
28776         this.loading.enable();
28777         this.updateInfo();
28778     },
28779
28780     // private
28781     getPageData : function(){
28782         var total = this.ds.getTotalCount();
28783         return {
28784             total : total,
28785             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28786             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28787         };
28788     },
28789
28790     // private
28791     onLoadError : function(proxy, o){
28792         this.loading.enable();
28793         if (this.ds.events.loadexception.listeners.length  < 2) {
28794             // nothing has been assigned to loadexception except this...
28795             // so 
28796             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
28797
28798         }
28799     },
28800
28801     // private
28802     onPagingKeydown : function(e){
28803         var k = e.getKey();
28804         var d = this.getPageData();
28805         if(k == e.RETURN){
28806             var v = this.field.dom.value, pageNum;
28807             if(!v || isNaN(pageNum = parseInt(v, 10))){
28808                 this.field.dom.value = d.activePage;
28809                 return;
28810             }
28811             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28812             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28813             e.stopEvent();
28814         }
28815         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))
28816         {
28817           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28818           this.field.dom.value = pageNum;
28819           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28820           e.stopEvent();
28821         }
28822         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28823         {
28824           var v = this.field.dom.value, pageNum; 
28825           var increment = (e.shiftKey) ? 10 : 1;
28826           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28827                 increment *= -1;
28828           }
28829           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28830             this.field.dom.value = d.activePage;
28831             return;
28832           }
28833           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28834           {
28835             this.field.dom.value = parseInt(v, 10) + increment;
28836             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28837             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28838           }
28839           e.stopEvent();
28840         }
28841     },
28842
28843     // private
28844     beforeLoad : function(){
28845         if(this.loading){
28846             this.loading.disable();
28847         }
28848     },
28849
28850     // private
28851     onClick : function(which){
28852         
28853         var ds = this.ds;
28854         if (!ds) {
28855             return;
28856         }
28857         
28858         switch(which){
28859             case "first":
28860                 ds.load({params:{start: 0, limit: this.pageSize}});
28861             break;
28862             case "prev":
28863                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28864             break;
28865             case "next":
28866                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28867             break;
28868             case "last":
28869                 var total = ds.getTotalCount();
28870                 var extra = total % this.pageSize;
28871                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28872                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28873             break;
28874             case "refresh":
28875                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28876             break;
28877         }
28878     },
28879
28880     /**
28881      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28882      * @param {Roo.data.Store} store The data store to unbind
28883      */
28884     unbind : function(ds){
28885         ds.un("beforeload", this.beforeLoad, this);
28886         ds.un("load", this.onLoad, this);
28887         ds.un("loadexception", this.onLoadError, this);
28888         ds.un("remove", this.updateInfo, this);
28889         ds.un("add", this.updateInfo, this);
28890         this.ds = undefined;
28891     },
28892
28893     /**
28894      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28895      * @param {Roo.data.Store} store The data store to bind
28896      */
28897     bind : function(ds){
28898         ds.on("beforeload", this.beforeLoad, this);
28899         ds.on("load", this.onLoad, this);
28900         ds.on("loadexception", this.onLoadError, this);
28901         ds.on("remove", this.updateInfo, this);
28902         ds.on("add", this.updateInfo, this);
28903         this.ds = ds;
28904     }
28905 });/*
28906  * - LGPL
28907  *
28908  * element
28909  * 
28910  */
28911
28912 /**
28913  * @class Roo.bootstrap.MessageBar
28914  * @extends Roo.bootstrap.Component
28915  * Bootstrap MessageBar class
28916  * @cfg {String} html contents of the MessageBar
28917  * @cfg {String} weight (info | success | warning | danger) default info
28918  * @cfg {String} beforeClass insert the bar before the given class
28919  * @cfg {Boolean} closable (true | false) default false
28920  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28921  * 
28922  * @constructor
28923  * Create a new Element
28924  * @param {Object} config The config object
28925  */
28926
28927 Roo.bootstrap.MessageBar = function(config){
28928     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28929 };
28930
28931 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28932     
28933     html: '',
28934     weight: 'info',
28935     closable: false,
28936     fixed: false,
28937     beforeClass: 'bootstrap-sticky-wrap',
28938     
28939     getAutoCreate : function(){
28940         
28941         var cfg = {
28942             tag: 'div',
28943             cls: 'alert alert-dismissable alert-' + this.weight,
28944             cn: [
28945                 {
28946                     tag: 'span',
28947                     cls: 'message',
28948                     html: this.html || ''
28949                 }
28950             ]
28951         };
28952         
28953         if(this.fixed){
28954             cfg.cls += ' alert-messages-fixed';
28955         }
28956         
28957         if(this.closable){
28958             cfg.cn.push({
28959                 tag: 'button',
28960                 cls: 'close',
28961                 html: 'x'
28962             });
28963         }
28964         
28965         return cfg;
28966     },
28967     
28968     onRender : function(ct, position)
28969     {
28970         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28971         
28972         if(!this.el){
28973             var cfg = Roo.apply({},  this.getAutoCreate());
28974             cfg.id = Roo.id();
28975             
28976             if (this.cls) {
28977                 cfg.cls += ' ' + this.cls;
28978             }
28979             if (this.style) {
28980                 cfg.style = this.style;
28981             }
28982             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28983             
28984             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28985         }
28986         
28987         this.el.select('>button.close').on('click', this.hide, this);
28988         
28989     },
28990     
28991     show : function()
28992     {
28993         if (!this.rendered) {
28994             this.render();
28995         }
28996         
28997         this.el.show();
28998         
28999         this.fireEvent('show', this);
29000         
29001     },
29002     
29003     hide : function()
29004     {
29005         if (!this.rendered) {
29006             this.render();
29007         }
29008         
29009         this.el.hide();
29010         
29011         this.fireEvent('hide', this);
29012     },
29013     
29014     update : function()
29015     {
29016 //        var e = this.el.dom.firstChild;
29017 //        
29018 //        if(this.closable){
29019 //            e = e.nextSibling;
29020 //        }
29021 //        
29022 //        e.data = this.html || '';
29023
29024         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29025     }
29026    
29027 });
29028
29029  
29030
29031      /*
29032  * - LGPL
29033  *
29034  * Graph
29035  * 
29036  */
29037
29038
29039 /**
29040  * @class Roo.bootstrap.Graph
29041  * @extends Roo.bootstrap.Component
29042  * Bootstrap Graph class
29043 > Prameters
29044  -sm {number} sm 4
29045  -md {number} md 5
29046  @cfg {String} graphtype  bar | vbar | pie
29047  @cfg {number} g_x coodinator | centre x (pie)
29048  @cfg {number} g_y coodinator | centre y (pie)
29049  @cfg {number} g_r radius (pie)
29050  @cfg {number} g_height height of the chart (respected by all elements in the set)
29051  @cfg {number} g_width width of the chart (respected by all elements in the set)
29052  @cfg {Object} title The title of the chart
29053     
29054  -{Array}  values
29055  -opts (object) options for the chart 
29056      o {
29057      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29058      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29059      o vgutter (number)
29060      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.
29061      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29062      o to
29063      o stretch (boolean)
29064      o }
29065  -opts (object) options for the pie
29066      o{
29067      o cut
29068      o startAngle (number)
29069      o endAngle (number)
29070      } 
29071  *
29072  * @constructor
29073  * Create a new Input
29074  * @param {Object} config The config object
29075  */
29076
29077 Roo.bootstrap.Graph = function(config){
29078     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29079     
29080     this.addEvents({
29081         // img events
29082         /**
29083          * @event click
29084          * The img click event for the img.
29085          * @param {Roo.EventObject} e
29086          */
29087         "click" : true
29088     });
29089 };
29090
29091 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29092     
29093     sm: 4,
29094     md: 5,
29095     graphtype: 'bar',
29096     g_height: 250,
29097     g_width: 400,
29098     g_x: 50,
29099     g_y: 50,
29100     g_r: 30,
29101     opts:{
29102         //g_colors: this.colors,
29103         g_type: 'soft',
29104         g_gutter: '20%'
29105
29106     },
29107     title : false,
29108
29109     getAutoCreate : function(){
29110         
29111         var cfg = {
29112             tag: 'div',
29113             html : null
29114         };
29115         
29116         
29117         return  cfg;
29118     },
29119
29120     onRender : function(ct,position){
29121         
29122         
29123         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29124         
29125         if (typeof(Raphael) == 'undefined') {
29126             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29127             return;
29128         }
29129         
29130         this.raphael = Raphael(this.el.dom);
29131         
29132                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29133                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29134                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29135                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29136                 /*
29137                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29138                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29139                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29140                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29141                 
29142                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29143                 r.barchart(330, 10, 300, 220, data1);
29144                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29145                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29146                 */
29147                 
29148                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29149                 // r.barchart(30, 30, 560, 250,  xdata, {
29150                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29151                 //     axis : "0 0 1 1",
29152                 //     axisxlabels :  xdata
29153                 //     //yvalues : cols,
29154                    
29155                 // });
29156 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29157 //        
29158 //        this.load(null,xdata,{
29159 //                axis : "0 0 1 1",
29160 //                axisxlabels :  xdata
29161 //                });
29162
29163     },
29164
29165     load : function(graphtype,xdata,opts)
29166     {
29167         this.raphael.clear();
29168         if(!graphtype) {
29169             graphtype = this.graphtype;
29170         }
29171         if(!opts){
29172             opts = this.opts;
29173         }
29174         var r = this.raphael,
29175             fin = function () {
29176                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29177             },
29178             fout = function () {
29179                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29180             },
29181             pfin = function() {
29182                 this.sector.stop();
29183                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29184
29185                 if (this.label) {
29186                     this.label[0].stop();
29187                     this.label[0].attr({ r: 7.5 });
29188                     this.label[1].attr({ "font-weight": 800 });
29189                 }
29190             },
29191             pfout = function() {
29192                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29193
29194                 if (this.label) {
29195                     this.label[0].animate({ r: 5 }, 500, "bounce");
29196                     this.label[1].attr({ "font-weight": 400 });
29197                 }
29198             };
29199
29200         switch(graphtype){
29201             case 'bar':
29202                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29203                 break;
29204             case 'hbar':
29205                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29206                 break;
29207             case 'pie':
29208 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29209 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29210 //            
29211                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29212                 
29213                 break;
29214
29215         }
29216         
29217         if(this.title){
29218             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29219         }
29220         
29221     },
29222     
29223     setTitle: function(o)
29224     {
29225         this.title = o;
29226     },
29227     
29228     initEvents: function() {
29229         
29230         if(!this.href){
29231             this.el.on('click', this.onClick, this);
29232         }
29233     },
29234     
29235     onClick : function(e)
29236     {
29237         Roo.log('img onclick');
29238         this.fireEvent('click', this, e);
29239     }
29240    
29241 });
29242
29243  
29244 /*
29245  * - LGPL
29246  *
29247  * numberBox
29248  * 
29249  */
29250 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29251
29252 /**
29253  * @class Roo.bootstrap.dash.NumberBox
29254  * @extends Roo.bootstrap.Component
29255  * Bootstrap NumberBox class
29256  * @cfg {String} headline Box headline
29257  * @cfg {String} content Box content
29258  * @cfg {String} icon Box icon
29259  * @cfg {String} footer Footer text
29260  * @cfg {String} fhref Footer href
29261  * 
29262  * @constructor
29263  * Create a new NumberBox
29264  * @param {Object} config The config object
29265  */
29266
29267
29268 Roo.bootstrap.dash.NumberBox = function(config){
29269     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29270     
29271 };
29272
29273 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29274     
29275     headline : '',
29276     content : '',
29277     icon : '',
29278     footer : '',
29279     fhref : '',
29280     ficon : '',
29281     
29282     getAutoCreate : function(){
29283         
29284         var cfg = {
29285             tag : 'div',
29286             cls : 'small-box ',
29287             cn : [
29288                 {
29289                     tag : 'div',
29290                     cls : 'inner',
29291                     cn :[
29292                         {
29293                             tag : 'h3',
29294                             cls : 'roo-headline',
29295                             html : this.headline
29296                         },
29297                         {
29298                             tag : 'p',
29299                             cls : 'roo-content',
29300                             html : this.content
29301                         }
29302                     ]
29303                 }
29304             ]
29305         };
29306         
29307         if(this.icon){
29308             cfg.cn.push({
29309                 tag : 'div',
29310                 cls : 'icon',
29311                 cn :[
29312                     {
29313                         tag : 'i',
29314                         cls : 'ion ' + this.icon
29315                     }
29316                 ]
29317             });
29318         }
29319         
29320         if(this.footer){
29321             var footer = {
29322                 tag : 'a',
29323                 cls : 'small-box-footer',
29324                 href : this.fhref || '#',
29325                 html : this.footer
29326             };
29327             
29328             cfg.cn.push(footer);
29329             
29330         }
29331         
29332         return  cfg;
29333     },
29334
29335     onRender : function(ct,position){
29336         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29337
29338
29339        
29340                 
29341     },
29342
29343     setHeadline: function (value)
29344     {
29345         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29346     },
29347     
29348     setFooter: function (value, href)
29349     {
29350         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29351         
29352         if(href){
29353             this.el.select('a.small-box-footer',true).first().attr('href', href);
29354         }
29355         
29356     },
29357
29358     setContent: function (value)
29359     {
29360         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29361     },
29362
29363     initEvents: function() 
29364     {   
29365         
29366     }
29367     
29368 });
29369
29370  
29371 /*
29372  * - LGPL
29373  *
29374  * TabBox
29375  * 
29376  */
29377 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29378
29379 /**
29380  * @class Roo.bootstrap.dash.TabBox
29381  * @extends Roo.bootstrap.Component
29382  * @children Roo.bootstrap.dash.TabPane
29383  * Bootstrap TabBox class
29384  * @cfg {String} title Title of the TabBox
29385  * @cfg {String} icon Icon of the TabBox
29386  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29387  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29388  * 
29389  * @constructor
29390  * Create a new TabBox
29391  * @param {Object} config The config object
29392  */
29393
29394
29395 Roo.bootstrap.dash.TabBox = function(config){
29396     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29397     this.addEvents({
29398         // raw events
29399         /**
29400          * @event addpane
29401          * When a pane is added
29402          * @param {Roo.bootstrap.dash.TabPane} pane
29403          */
29404         "addpane" : true,
29405         /**
29406          * @event activatepane
29407          * When a pane is activated
29408          * @param {Roo.bootstrap.dash.TabPane} pane
29409          */
29410         "activatepane" : true
29411         
29412          
29413     });
29414     
29415     this.panes = [];
29416 };
29417
29418 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29419
29420     title : '',
29421     icon : false,
29422     showtabs : true,
29423     tabScrollable : false,
29424     
29425     getChildContainer : function()
29426     {
29427         return this.el.select('.tab-content', true).first();
29428     },
29429     
29430     getAutoCreate : function(){
29431         
29432         var header = {
29433             tag: 'li',
29434             cls: 'pull-left header',
29435             html: this.title,
29436             cn : []
29437         };
29438         
29439         if(this.icon){
29440             header.cn.push({
29441                 tag: 'i',
29442                 cls: 'fa ' + this.icon
29443             });
29444         }
29445         
29446         var h = {
29447             tag: 'ul',
29448             cls: 'nav nav-tabs pull-right',
29449             cn: [
29450                 header
29451             ]
29452         };
29453         
29454         if(this.tabScrollable){
29455             h = {
29456                 tag: 'div',
29457                 cls: 'tab-header',
29458                 cn: [
29459                     {
29460                         tag: 'ul',
29461                         cls: 'nav nav-tabs pull-right',
29462                         cn: [
29463                             header
29464                         ]
29465                     }
29466                 ]
29467             };
29468         }
29469         
29470         var cfg = {
29471             tag: 'div',
29472             cls: 'nav-tabs-custom',
29473             cn: [
29474                 h,
29475                 {
29476                     tag: 'div',
29477                     cls: 'tab-content no-padding',
29478                     cn: []
29479                 }
29480             ]
29481         };
29482
29483         return  cfg;
29484     },
29485     initEvents : function()
29486     {
29487         //Roo.log('add add pane handler');
29488         this.on('addpane', this.onAddPane, this);
29489     },
29490      /**
29491      * Updates the box title
29492      * @param {String} html to set the title to.
29493      */
29494     setTitle : function(value)
29495     {
29496         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29497     },
29498     onAddPane : function(pane)
29499     {
29500         this.panes.push(pane);
29501         //Roo.log('addpane');
29502         //Roo.log(pane);
29503         // tabs are rendere left to right..
29504         if(!this.showtabs){
29505             return;
29506         }
29507         
29508         var ctr = this.el.select('.nav-tabs', true).first();
29509          
29510          
29511         var existing = ctr.select('.nav-tab',true);
29512         var qty = existing.getCount();;
29513         
29514         
29515         var tab = ctr.createChild({
29516             tag : 'li',
29517             cls : 'nav-tab' + (qty ? '' : ' active'),
29518             cn : [
29519                 {
29520                     tag : 'a',
29521                     href:'#',
29522                     html : pane.title
29523                 }
29524             ]
29525         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29526         pane.tab = tab;
29527         
29528         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29529         if (!qty) {
29530             pane.el.addClass('active');
29531         }
29532         
29533                 
29534     },
29535     onTabClick : function(ev,un,ob,pane)
29536     {
29537         //Roo.log('tab - prev default');
29538         ev.preventDefault();
29539         
29540         
29541         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29542         pane.tab.addClass('active');
29543         //Roo.log(pane.title);
29544         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29545         // technically we should have a deactivate event.. but maybe add later.
29546         // and it should not de-activate the selected tab...
29547         this.fireEvent('activatepane', pane);
29548         pane.el.addClass('active');
29549         pane.fireEvent('activate');
29550         
29551         
29552     },
29553     
29554     getActivePane : function()
29555     {
29556         var r = false;
29557         Roo.each(this.panes, function(p) {
29558             if(p.el.hasClass('active')){
29559                 r = p;
29560                 return false;
29561             }
29562             
29563             return;
29564         });
29565         
29566         return r;
29567     }
29568     
29569     
29570 });
29571
29572  
29573 /*
29574  * - LGPL
29575  *
29576  * Tab pane
29577  * 
29578  */
29579 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29580 /**
29581  * @class Roo.bootstrap.TabPane
29582  * @extends Roo.bootstrap.Component
29583  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29584  * Bootstrap TabPane class
29585  * @cfg {Boolean} active (false | true) Default false
29586  * @cfg {String} title title of panel
29587
29588  * 
29589  * @constructor
29590  * Create a new TabPane
29591  * @param {Object} config The config object
29592  */
29593
29594 Roo.bootstrap.dash.TabPane = function(config){
29595     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29596     
29597     this.addEvents({
29598         // raw events
29599         /**
29600          * @event activate
29601          * When a pane is activated
29602          * @param {Roo.bootstrap.dash.TabPane} pane
29603          */
29604         "activate" : true
29605          
29606     });
29607 };
29608
29609 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29610     
29611     active : false,
29612     title : '',
29613     
29614     // the tabBox that this is attached to.
29615     tab : false,
29616      
29617     getAutoCreate : function() 
29618     {
29619         var cfg = {
29620             tag: 'div',
29621             cls: 'tab-pane'
29622         };
29623         
29624         if(this.active){
29625             cfg.cls += ' active';
29626         }
29627         
29628         return cfg;
29629     },
29630     initEvents  : function()
29631     {
29632         //Roo.log('trigger add pane handler');
29633         this.parent().fireEvent('addpane', this)
29634     },
29635     
29636      /**
29637      * Updates the tab title 
29638      * @param {String} html to set the title to.
29639      */
29640     setTitle: function(str)
29641     {
29642         if (!this.tab) {
29643             return;
29644         }
29645         this.title = str;
29646         this.tab.select('a', true).first().dom.innerHTML = str;
29647         
29648     }
29649     
29650     
29651     
29652 });
29653
29654  
29655
29656
29657  /*
29658  * - LGPL
29659  *
29660  * Tooltip
29661  * 
29662  */
29663
29664 /**
29665  * @class Roo.bootstrap.Tooltip
29666  * Bootstrap Tooltip class
29667  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29668  * to determine which dom element triggers the tooltip.
29669  * 
29670  * It needs to add support for additional attributes like tooltip-position
29671  * 
29672  * @constructor
29673  * Create a new Toolti
29674  * @param {Object} config The config object
29675  */
29676
29677 Roo.bootstrap.Tooltip = function(config){
29678     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29679     
29680     this.alignment = Roo.bootstrap.Tooltip.alignment;
29681     
29682     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29683         this.alignment = config.alignment;
29684     }
29685     
29686 };
29687
29688 Roo.apply(Roo.bootstrap.Tooltip, {
29689     /**
29690      * @function init initialize tooltip monitoring.
29691      * @static
29692      */
29693     currentEl : false,
29694     currentTip : false,
29695     currentRegion : false,
29696     
29697     //  init : delay?
29698     
29699     init : function()
29700     {
29701         Roo.get(document).on('mouseover', this.enter ,this);
29702         Roo.get(document).on('mouseout', this.leave, this);
29703          
29704         
29705         this.currentTip = new Roo.bootstrap.Tooltip();
29706     },
29707     
29708     enter : function(ev)
29709     {
29710         var dom = ev.getTarget();
29711         
29712         //Roo.log(['enter',dom]);
29713         var el = Roo.fly(dom);
29714         if (this.currentEl) {
29715             //Roo.log(dom);
29716             //Roo.log(this.currentEl);
29717             //Roo.log(this.currentEl.contains(dom));
29718             if (this.currentEl == el) {
29719                 return;
29720             }
29721             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29722                 return;
29723             }
29724
29725         }
29726         
29727         if (this.currentTip.el) {
29728             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29729         }    
29730         //Roo.log(ev);
29731         
29732         if(!el || el.dom == document){
29733             return;
29734         }
29735         
29736         var bindEl = el; 
29737         var pel = false;
29738         if (!el.attr('tooltip')) {
29739             pel = el.findParent("[tooltip]");
29740             if (pel) {
29741                 bindEl = Roo.get(pel);
29742             }
29743         }
29744         
29745        
29746         
29747         // you can not look for children, as if el is the body.. then everythign is the child..
29748         if (!pel && !el.attr('tooltip')) { //
29749             if (!el.select("[tooltip]").elements.length) {
29750                 return;
29751             }
29752             // is the mouse over this child...?
29753             bindEl = el.select("[tooltip]").first();
29754             var xy = ev.getXY();
29755             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29756                 //Roo.log("not in region.");
29757                 return;
29758             }
29759             //Roo.log("child element over..");
29760             
29761         }
29762         this.currentEl = el;
29763         this.currentTip.bind(bindEl);
29764         this.currentRegion = Roo.lib.Region.getRegion(dom);
29765         this.currentTip.enter();
29766         
29767     },
29768     leave : function(ev)
29769     {
29770         var dom = ev.getTarget();
29771         //Roo.log(['leave',dom]);
29772         if (!this.currentEl) {
29773             return;
29774         }
29775         
29776         
29777         if (dom != this.currentEl.dom) {
29778             return;
29779         }
29780         var xy = ev.getXY();
29781         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29782             return;
29783         }
29784         // only activate leave if mouse cursor is outside... bounding box..
29785         
29786         
29787         
29788         
29789         if (this.currentTip) {
29790             this.currentTip.leave();
29791         }
29792         //Roo.log('clear currentEl');
29793         this.currentEl = false;
29794         
29795         
29796     },
29797     alignment : {
29798         'left' : ['r-l', [-2,0], 'right'],
29799         'right' : ['l-r', [2,0], 'left'],
29800         'bottom' : ['t-b', [0,2], 'top'],
29801         'top' : [ 'b-t', [0,-2], 'bottom']
29802     }
29803     
29804 });
29805
29806
29807 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29808     
29809     
29810     bindEl : false,
29811     
29812     delay : null, // can be { show : 300 , hide: 500}
29813     
29814     timeout : null,
29815     
29816     hoverState : null, //???
29817     
29818     placement : 'bottom', 
29819     
29820     alignment : false,
29821     
29822     getAutoCreate : function(){
29823     
29824         var cfg = {
29825            cls : 'tooltip',   
29826            role : 'tooltip',
29827            cn : [
29828                 {
29829                     cls : 'tooltip-arrow arrow'
29830                 },
29831                 {
29832                     cls : 'tooltip-inner'
29833                 }
29834            ]
29835         };
29836         
29837         return cfg;
29838     },
29839     bind : function(el)
29840     {
29841         this.bindEl = el;
29842     },
29843     
29844     initEvents : function()
29845     {
29846         this.arrowEl = this.el.select('.arrow', true).first();
29847         this.innerEl = this.el.select('.tooltip-inner', true).first();
29848     },
29849     
29850     enter : function () {
29851        
29852         if (this.timeout != null) {
29853             clearTimeout(this.timeout);
29854         }
29855         
29856         this.hoverState = 'in';
29857          //Roo.log("enter - show");
29858         if (!this.delay || !this.delay.show) {
29859             this.show();
29860             return;
29861         }
29862         var _t = this;
29863         this.timeout = setTimeout(function () {
29864             if (_t.hoverState == 'in') {
29865                 _t.show();
29866             }
29867         }, this.delay.show);
29868     },
29869     leave : function()
29870     {
29871         clearTimeout(this.timeout);
29872     
29873         this.hoverState = 'out';
29874          if (!this.delay || !this.delay.hide) {
29875             this.hide();
29876             return;
29877         }
29878        
29879         var _t = this;
29880         this.timeout = setTimeout(function () {
29881             //Roo.log("leave - timeout");
29882             
29883             if (_t.hoverState == 'out') {
29884                 _t.hide();
29885                 Roo.bootstrap.Tooltip.currentEl = false;
29886             }
29887         }, delay);
29888     },
29889     
29890     show : function (msg)
29891     {
29892         if (!this.el) {
29893             this.render(document.body);
29894         }
29895         // set content.
29896         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29897         
29898         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29899         
29900         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29901         
29902         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29903                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29904         
29905         var placement = typeof this.placement == 'function' ?
29906             this.placement.call(this, this.el, on_el) :
29907             this.placement;
29908             
29909         var autoToken = /\s?auto?\s?/i;
29910         var autoPlace = autoToken.test(placement);
29911         if (autoPlace) {
29912             placement = placement.replace(autoToken, '') || 'top';
29913         }
29914         
29915         //this.el.detach()
29916         //this.el.setXY([0,0]);
29917         this.el.show();
29918         //this.el.dom.style.display='block';
29919         
29920         //this.el.appendTo(on_el);
29921         
29922         var p = this.getPosition();
29923         var box = this.el.getBox();
29924         
29925         if (autoPlace) {
29926             // fixme..
29927         }
29928         
29929         var align = this.alignment[placement];
29930         
29931         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29932         
29933         if(placement == 'top' || placement == 'bottom'){
29934             if(xy[0] < 0){
29935                 placement = 'right';
29936             }
29937             
29938             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29939                 placement = 'left';
29940             }
29941             
29942             var scroll = Roo.select('body', true).first().getScroll();
29943             
29944             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29945                 placement = 'top';
29946             }
29947             
29948             align = this.alignment[placement];
29949             
29950             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29951             
29952         }
29953         
29954         var elems = document.getElementsByTagName('div');
29955         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29956         for (var i = 0; i < elems.length; i++) {
29957           var zindex = Number.parseInt(
29958                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29959                 10
29960           );
29961           if (zindex > highest) {
29962             highest = zindex;
29963           }
29964         }
29965         
29966         
29967         
29968         this.el.dom.style.zIndex = highest;
29969         
29970         this.el.alignTo(this.bindEl, align[0],align[1]);
29971         //var arrow = this.el.select('.arrow',true).first();
29972         //arrow.set(align[2], 
29973         
29974         this.el.addClass(placement);
29975         this.el.addClass("bs-tooltip-"+ placement);
29976         
29977         this.el.addClass('in fade show');
29978         
29979         this.hoverState = null;
29980         
29981         if (this.el.hasClass('fade')) {
29982             // fade it?
29983         }
29984         
29985         
29986         
29987         
29988         
29989     },
29990     hide : function()
29991     {
29992          
29993         if (!this.el) {
29994             return;
29995         }
29996         //this.el.setXY([0,0]);
29997         this.el.removeClass(['show', 'in']);
29998         //this.el.hide();
29999         
30000     }
30001     
30002 });
30003  
30004
30005  /*
30006  * - LGPL
30007  *
30008  * Location Picker
30009  * 
30010  */
30011
30012 /**
30013  * @class Roo.bootstrap.LocationPicker
30014  * @extends Roo.bootstrap.Component
30015  * Bootstrap LocationPicker class
30016  * @cfg {Number} latitude Position when init default 0
30017  * @cfg {Number} longitude Position when init default 0
30018  * @cfg {Number} zoom default 15
30019  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30020  * @cfg {Boolean} mapTypeControl default false
30021  * @cfg {Boolean} disableDoubleClickZoom default false
30022  * @cfg {Boolean} scrollwheel default true
30023  * @cfg {Boolean} streetViewControl default false
30024  * @cfg {Number} radius default 0
30025  * @cfg {String} locationName
30026  * @cfg {Boolean} draggable default true
30027  * @cfg {Boolean} enableAutocomplete default false
30028  * @cfg {Boolean} enableReverseGeocode default true
30029  * @cfg {String} markerTitle
30030  * 
30031  * @constructor
30032  * Create a new LocationPicker
30033  * @param {Object} config The config object
30034  */
30035
30036
30037 Roo.bootstrap.LocationPicker = function(config){
30038     
30039     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30040     
30041     this.addEvents({
30042         /**
30043          * @event initial
30044          * Fires when the picker initialized.
30045          * @param {Roo.bootstrap.LocationPicker} this
30046          * @param {Google Location} location
30047          */
30048         initial : true,
30049         /**
30050          * @event positionchanged
30051          * Fires when the picker position changed.
30052          * @param {Roo.bootstrap.LocationPicker} this
30053          * @param {Google Location} location
30054          */
30055         positionchanged : true,
30056         /**
30057          * @event resize
30058          * Fires when the map resize.
30059          * @param {Roo.bootstrap.LocationPicker} this
30060          */
30061         resize : true,
30062         /**
30063          * @event show
30064          * Fires when the map show.
30065          * @param {Roo.bootstrap.LocationPicker} this
30066          */
30067         show : true,
30068         /**
30069          * @event hide
30070          * Fires when the map hide.
30071          * @param {Roo.bootstrap.LocationPicker} this
30072          */
30073         hide : true,
30074         /**
30075          * @event mapClick
30076          * Fires when click the map.
30077          * @param {Roo.bootstrap.LocationPicker} this
30078          * @param {Map event} e
30079          */
30080         mapClick : true,
30081         /**
30082          * @event mapRightClick
30083          * Fires when right click the map.
30084          * @param {Roo.bootstrap.LocationPicker} this
30085          * @param {Map event} e
30086          */
30087         mapRightClick : true,
30088         /**
30089          * @event markerClick
30090          * Fires when click the marker.
30091          * @param {Roo.bootstrap.LocationPicker} this
30092          * @param {Map event} e
30093          */
30094         markerClick : true,
30095         /**
30096          * @event markerRightClick
30097          * Fires when right click the marker.
30098          * @param {Roo.bootstrap.LocationPicker} this
30099          * @param {Map event} e
30100          */
30101         markerRightClick : true,
30102         /**
30103          * @event OverlayViewDraw
30104          * Fires when OverlayView Draw
30105          * @param {Roo.bootstrap.LocationPicker} this
30106          */
30107         OverlayViewDraw : true,
30108         /**
30109          * @event OverlayViewOnAdd
30110          * Fires when OverlayView Draw
30111          * @param {Roo.bootstrap.LocationPicker} this
30112          */
30113         OverlayViewOnAdd : true,
30114         /**
30115          * @event OverlayViewOnRemove
30116          * Fires when OverlayView Draw
30117          * @param {Roo.bootstrap.LocationPicker} this
30118          */
30119         OverlayViewOnRemove : true,
30120         /**
30121          * @event OverlayViewShow
30122          * Fires when OverlayView Draw
30123          * @param {Roo.bootstrap.LocationPicker} this
30124          * @param {Pixel} cpx
30125          */
30126         OverlayViewShow : true,
30127         /**
30128          * @event OverlayViewHide
30129          * Fires when OverlayView Draw
30130          * @param {Roo.bootstrap.LocationPicker} this
30131          */
30132         OverlayViewHide : true,
30133         /**
30134          * @event loadexception
30135          * Fires when load google lib failed.
30136          * @param {Roo.bootstrap.LocationPicker} this
30137          */
30138         loadexception : true
30139     });
30140         
30141 };
30142
30143 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30144     
30145     gMapContext: false,
30146     
30147     latitude: 0,
30148     longitude: 0,
30149     zoom: 15,
30150     mapTypeId: false,
30151     mapTypeControl: false,
30152     disableDoubleClickZoom: false,
30153     scrollwheel: true,
30154     streetViewControl: false,
30155     radius: 0,
30156     locationName: '',
30157     draggable: true,
30158     enableAutocomplete: false,
30159     enableReverseGeocode: true,
30160     markerTitle: '',
30161     
30162     getAutoCreate: function()
30163     {
30164
30165         var cfg = {
30166             tag: 'div',
30167             cls: 'roo-location-picker'
30168         };
30169         
30170         return cfg
30171     },
30172     
30173     initEvents: function(ct, position)
30174     {       
30175         if(!this.el.getWidth() || this.isApplied()){
30176             return;
30177         }
30178         
30179         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30180         
30181         this.initial();
30182     },
30183     
30184     initial: function()
30185     {
30186         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30187             this.fireEvent('loadexception', this);
30188             return;
30189         }
30190         
30191         if(!this.mapTypeId){
30192             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30193         }
30194         
30195         this.gMapContext = this.GMapContext();
30196         
30197         this.initOverlayView();
30198         
30199         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30200         
30201         var _this = this;
30202                 
30203         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30204             _this.setPosition(_this.gMapContext.marker.position);
30205         });
30206         
30207         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30208             _this.fireEvent('mapClick', this, event);
30209             
30210         });
30211
30212         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30213             _this.fireEvent('mapRightClick', this, event);
30214             
30215         });
30216         
30217         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30218             _this.fireEvent('markerClick', this, event);
30219             
30220         });
30221
30222         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30223             _this.fireEvent('markerRightClick', this, event);
30224             
30225         });
30226         
30227         this.setPosition(this.gMapContext.location);
30228         
30229         this.fireEvent('initial', this, this.gMapContext.location);
30230     },
30231     
30232     initOverlayView: function()
30233     {
30234         var _this = this;
30235         
30236         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30237             
30238             draw: function()
30239             {
30240                 _this.fireEvent('OverlayViewDraw', _this);
30241             },
30242             
30243             onAdd: function()
30244             {
30245                 _this.fireEvent('OverlayViewOnAdd', _this);
30246             },
30247             
30248             onRemove: function()
30249             {
30250                 _this.fireEvent('OverlayViewOnRemove', _this);
30251             },
30252             
30253             show: function(cpx)
30254             {
30255                 _this.fireEvent('OverlayViewShow', _this, cpx);
30256             },
30257             
30258             hide: function()
30259             {
30260                 _this.fireEvent('OverlayViewHide', _this);
30261             }
30262             
30263         });
30264     },
30265     
30266     fromLatLngToContainerPixel: function(event)
30267     {
30268         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30269     },
30270     
30271     isApplied: function() 
30272     {
30273         return this.getGmapContext() == false ? false : true;
30274     },
30275     
30276     getGmapContext: function() 
30277     {
30278         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30279     },
30280     
30281     GMapContext: function() 
30282     {
30283         var position = new google.maps.LatLng(this.latitude, this.longitude);
30284         
30285         var _map = new google.maps.Map(this.el.dom, {
30286             center: position,
30287             zoom: this.zoom,
30288             mapTypeId: this.mapTypeId,
30289             mapTypeControl: this.mapTypeControl,
30290             disableDoubleClickZoom: this.disableDoubleClickZoom,
30291             scrollwheel: this.scrollwheel,
30292             streetViewControl: this.streetViewControl,
30293             locationName: this.locationName,
30294             draggable: this.draggable,
30295             enableAutocomplete: this.enableAutocomplete,
30296             enableReverseGeocode: this.enableReverseGeocode
30297         });
30298         
30299         var _marker = new google.maps.Marker({
30300             position: position,
30301             map: _map,
30302             title: this.markerTitle,
30303             draggable: this.draggable
30304         });
30305         
30306         return {
30307             map: _map,
30308             marker: _marker,
30309             circle: null,
30310             location: position,
30311             radius: this.radius,
30312             locationName: this.locationName,
30313             addressComponents: {
30314                 formatted_address: null,
30315                 addressLine1: null,
30316                 addressLine2: null,
30317                 streetName: null,
30318                 streetNumber: null,
30319                 city: null,
30320                 district: null,
30321                 state: null,
30322                 stateOrProvince: null
30323             },
30324             settings: this,
30325             domContainer: this.el.dom,
30326             geodecoder: new google.maps.Geocoder()
30327         };
30328     },
30329     
30330     drawCircle: function(center, radius, options) 
30331     {
30332         if (this.gMapContext.circle != null) {
30333             this.gMapContext.circle.setMap(null);
30334         }
30335         if (radius > 0) {
30336             radius *= 1;
30337             options = Roo.apply({}, options, {
30338                 strokeColor: "#0000FF",
30339                 strokeOpacity: .35,
30340                 strokeWeight: 2,
30341                 fillColor: "#0000FF",
30342                 fillOpacity: .2
30343             });
30344             
30345             options.map = this.gMapContext.map;
30346             options.radius = radius;
30347             options.center = center;
30348             this.gMapContext.circle = new google.maps.Circle(options);
30349             return this.gMapContext.circle;
30350         }
30351         
30352         return null;
30353     },
30354     
30355     setPosition: function(location) 
30356     {
30357         this.gMapContext.location = location;
30358         this.gMapContext.marker.setPosition(location);
30359         this.gMapContext.map.panTo(location);
30360         this.drawCircle(location, this.gMapContext.radius, {});
30361         
30362         var _this = this;
30363         
30364         if (this.gMapContext.settings.enableReverseGeocode) {
30365             this.gMapContext.geodecoder.geocode({
30366                 latLng: this.gMapContext.location
30367             }, function(results, status) {
30368                 
30369                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30370                     _this.gMapContext.locationName = results[0].formatted_address;
30371                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30372                     
30373                     _this.fireEvent('positionchanged', this, location);
30374                 }
30375             });
30376             
30377             return;
30378         }
30379         
30380         this.fireEvent('positionchanged', this, location);
30381     },
30382     
30383     resize: function()
30384     {
30385         google.maps.event.trigger(this.gMapContext.map, "resize");
30386         
30387         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30388         
30389         this.fireEvent('resize', this);
30390     },
30391     
30392     setPositionByLatLng: function(latitude, longitude)
30393     {
30394         this.setPosition(new google.maps.LatLng(latitude, longitude));
30395     },
30396     
30397     getCurrentPosition: function() 
30398     {
30399         return {
30400             latitude: this.gMapContext.location.lat(),
30401             longitude: this.gMapContext.location.lng()
30402         };
30403     },
30404     
30405     getAddressName: function() 
30406     {
30407         return this.gMapContext.locationName;
30408     },
30409     
30410     getAddressComponents: function() 
30411     {
30412         return this.gMapContext.addressComponents;
30413     },
30414     
30415     address_component_from_google_geocode: function(address_components) 
30416     {
30417         var result = {};
30418         
30419         for (var i = 0; i < address_components.length; i++) {
30420             var component = address_components[i];
30421             if (component.types.indexOf("postal_code") >= 0) {
30422                 result.postalCode = component.short_name;
30423             } else if (component.types.indexOf("street_number") >= 0) {
30424                 result.streetNumber = component.short_name;
30425             } else if (component.types.indexOf("route") >= 0) {
30426                 result.streetName = component.short_name;
30427             } else if (component.types.indexOf("neighborhood") >= 0) {
30428                 result.city = component.short_name;
30429             } else if (component.types.indexOf("locality") >= 0) {
30430                 result.city = component.short_name;
30431             } else if (component.types.indexOf("sublocality") >= 0) {
30432                 result.district = component.short_name;
30433             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30434                 result.stateOrProvince = component.short_name;
30435             } else if (component.types.indexOf("country") >= 0) {
30436                 result.country = component.short_name;
30437             }
30438         }
30439         
30440         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30441         result.addressLine2 = "";
30442         return result;
30443     },
30444     
30445     setZoomLevel: function(zoom)
30446     {
30447         this.gMapContext.map.setZoom(zoom);
30448     },
30449     
30450     show: function()
30451     {
30452         if(!this.el){
30453             return;
30454         }
30455         
30456         this.el.show();
30457         
30458         this.resize();
30459         
30460         this.fireEvent('show', this);
30461     },
30462     
30463     hide: function()
30464     {
30465         if(!this.el){
30466             return;
30467         }
30468         
30469         this.el.hide();
30470         
30471         this.fireEvent('hide', this);
30472     }
30473     
30474 });
30475
30476 Roo.apply(Roo.bootstrap.LocationPicker, {
30477     
30478     OverlayView : function(map, options)
30479     {
30480         options = options || {};
30481         
30482         this.setMap(map);
30483     }
30484     
30485     
30486 });/**
30487  * @class Roo.bootstrap.Alert
30488  * @extends Roo.bootstrap.Component
30489  * Bootstrap Alert class - shows an alert area box
30490  * eg
30491  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30492   Enter a valid email address
30493 </div>
30494  * @licence LGPL
30495  * @cfg {String} title The title of alert
30496  * @cfg {String} html The content of alert
30497  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30498  * @cfg {String} fa font-awesomeicon
30499  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30500  * @cfg {Boolean} close true to show a x closer
30501  * 
30502  * 
30503  * @constructor
30504  * Create a new alert
30505  * @param {Object} config The config object
30506  */
30507
30508
30509 Roo.bootstrap.Alert = function(config){
30510     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30511     
30512 };
30513
30514 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30515     
30516     title: '',
30517     html: '',
30518     weight: false,
30519     fa: false,
30520     faicon: false, // BC
30521     close : false,
30522     
30523     
30524     getAutoCreate : function()
30525     {
30526         
30527         var cfg = {
30528             tag : 'div',
30529             cls : 'alert',
30530             cn : [
30531                 {
30532                     tag: 'button',
30533                     type :  "button",
30534                     cls: "close",
30535                     html : '×',
30536                     style : this.close ? '' : 'display:none'
30537                 },
30538                 {
30539                     tag : 'i',
30540                     cls : 'roo-alert-icon'
30541                     
30542                 },
30543                 {
30544                     tag : 'b',
30545                     cls : 'roo-alert-title',
30546                     html : this.title
30547                 },
30548                 {
30549                     tag : 'span',
30550                     cls : 'roo-alert-text',
30551                     html : this.html
30552                 }
30553             ]
30554         };
30555         
30556         if(this.faicon){
30557             cfg.cn[0].cls += ' fa ' + this.faicon;
30558         }
30559         if(this.fa){
30560             cfg.cn[0].cls += ' fa ' + this.fa;
30561         }
30562         
30563         if(this.weight){
30564             cfg.cls += ' alert-' + this.weight;
30565         }
30566         
30567         return cfg;
30568     },
30569     
30570     initEvents: function() 
30571     {
30572         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30573         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30574         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30575         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30576         if (this.seconds > 0) {
30577             this.hide.defer(this.seconds, this);
30578         }
30579     },
30580     /**
30581      * Set the Title Message HTML
30582      * @param {String} html
30583      */
30584     setTitle : function(str)
30585     {
30586         this.titleEl.dom.innerHTML = str;
30587     },
30588      
30589      /**
30590      * Set the Body Message HTML
30591      * @param {String} html
30592      */
30593     setHtml : function(str)
30594     {
30595         this.htmlEl.dom.innerHTML = str;
30596     },
30597     /**
30598      * Set the Weight of the alert
30599      * @param {String} (success|info|warning|danger) weight
30600      */
30601     
30602     setWeight : function(weight)
30603     {
30604         if(this.weight){
30605             this.el.removeClass('alert-' + this.weight);
30606         }
30607         
30608         this.weight = weight;
30609         
30610         this.el.addClass('alert-' + this.weight);
30611     },
30612       /**
30613      * Set the Icon of the alert
30614      * @param {String} see fontawsome names (name without the 'fa-' bit)
30615      */
30616     setIcon : function(icon)
30617     {
30618         if(this.faicon){
30619             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30620         }
30621         
30622         this.faicon = icon;
30623         
30624         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30625     },
30626     /**
30627      * Hide the Alert
30628      */
30629     hide: function() 
30630     {
30631         this.el.hide();   
30632     },
30633     /**
30634      * Show the Alert
30635      */
30636     show: function() 
30637     {  
30638         this.el.show();   
30639     }
30640     
30641 });
30642
30643  
30644 /*
30645 * Licence: LGPL
30646 */
30647
30648 /**
30649  * @class Roo.bootstrap.UploadCropbox
30650  * @extends Roo.bootstrap.Component
30651  * Bootstrap UploadCropbox class
30652  * @cfg {String} emptyText show when image has been loaded
30653  * @cfg {String} rotateNotify show when image too small to rotate
30654  * @cfg {Number} errorTimeout default 3000
30655  * @cfg {Number} minWidth default 300
30656  * @cfg {Number} minHeight default 300
30657  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30658  * @cfg {Boolean} isDocument (true|false) default false
30659  * @cfg {String} url action url
30660  * @cfg {String} paramName default 'imageUpload'
30661  * @cfg {String} method default POST
30662  * @cfg {Boolean} loadMask (true|false) default true
30663  * @cfg {Boolean} loadingText default 'Loading...'
30664  * 
30665  * @constructor
30666  * Create a new UploadCropbox
30667  * @param {Object} config The config object
30668  */
30669
30670 Roo.bootstrap.UploadCropbox = function(config){
30671     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30672     
30673     this.addEvents({
30674         /**
30675          * @event beforeselectfile
30676          * Fire before select file
30677          * @param {Roo.bootstrap.UploadCropbox} this
30678          */
30679         "beforeselectfile" : true,
30680         /**
30681          * @event initial
30682          * Fire after initEvent
30683          * @param {Roo.bootstrap.UploadCropbox} this
30684          */
30685         "initial" : true,
30686         /**
30687          * @event crop
30688          * Fire after initEvent
30689          * @param {Roo.bootstrap.UploadCropbox} this
30690          * @param {String} data
30691          */
30692         "crop" : true,
30693         /**
30694          * @event prepare
30695          * Fire when preparing the file data
30696          * @param {Roo.bootstrap.UploadCropbox} this
30697          * @param {Object} file
30698          */
30699         "prepare" : true,
30700         /**
30701          * @event exception
30702          * Fire when get exception
30703          * @param {Roo.bootstrap.UploadCropbox} this
30704          * @param {XMLHttpRequest} xhr
30705          */
30706         "exception" : true,
30707         /**
30708          * @event beforeloadcanvas
30709          * Fire before load the canvas
30710          * @param {Roo.bootstrap.UploadCropbox} this
30711          * @param {String} src
30712          */
30713         "beforeloadcanvas" : true,
30714         /**
30715          * @event trash
30716          * Fire when trash image
30717          * @param {Roo.bootstrap.UploadCropbox} this
30718          */
30719         "trash" : true,
30720         /**
30721          * @event download
30722          * Fire when download the image
30723          * @param {Roo.bootstrap.UploadCropbox} this
30724          */
30725         "download" : true,
30726         /**
30727          * @event footerbuttonclick
30728          * Fire when footerbuttonclick
30729          * @param {Roo.bootstrap.UploadCropbox} this
30730          * @param {String} type
30731          */
30732         "footerbuttonclick" : true,
30733         /**
30734          * @event resize
30735          * Fire when resize
30736          * @param {Roo.bootstrap.UploadCropbox} this
30737          */
30738         "resize" : true,
30739         /**
30740          * @event rotate
30741          * Fire when rotate the image
30742          * @param {Roo.bootstrap.UploadCropbox} this
30743          * @param {String} pos
30744          */
30745         "rotate" : true,
30746         /**
30747          * @event inspect
30748          * Fire when inspect the file
30749          * @param {Roo.bootstrap.UploadCropbox} this
30750          * @param {Object} file
30751          */
30752         "inspect" : true,
30753         /**
30754          * @event upload
30755          * Fire when xhr upload the file
30756          * @param {Roo.bootstrap.UploadCropbox} this
30757          * @param {Object} data
30758          */
30759         "upload" : true,
30760         /**
30761          * @event arrange
30762          * Fire when arrange the file data
30763          * @param {Roo.bootstrap.UploadCropbox} this
30764          * @param {Object} formData
30765          */
30766         "arrange" : true
30767     });
30768     
30769     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30770 };
30771
30772 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30773     
30774     emptyText : 'Click to upload image',
30775     rotateNotify : 'Image is too small to rotate',
30776     errorTimeout : 3000,
30777     scale : 0,
30778     baseScale : 1,
30779     rotate : 0,
30780     dragable : false,
30781     pinching : false,
30782     mouseX : 0,
30783     mouseY : 0,
30784     cropData : false,
30785     minWidth : 300,
30786     minHeight : 300,
30787     file : false,
30788     exif : {},
30789     baseRotate : 1,
30790     cropType : 'image/jpeg',
30791     buttons : false,
30792     canvasLoaded : false,
30793     isDocument : false,
30794     method : 'POST',
30795     paramName : 'imageUpload',
30796     loadMask : true,
30797     loadingText : 'Loading...',
30798     maskEl : false,
30799     
30800     getAutoCreate : function()
30801     {
30802         var cfg = {
30803             tag : 'div',
30804             cls : 'roo-upload-cropbox',
30805             cn : [
30806                 {
30807                     tag : 'input',
30808                     cls : 'roo-upload-cropbox-selector',
30809                     type : 'file'
30810                 },
30811                 {
30812                     tag : 'div',
30813                     cls : 'roo-upload-cropbox-body',
30814                     style : 'cursor:pointer',
30815                     cn : [
30816                         {
30817                             tag : 'div',
30818                             cls : 'roo-upload-cropbox-preview'
30819                         },
30820                         {
30821                             tag : 'div',
30822                             cls : 'roo-upload-cropbox-thumb'
30823                         },
30824                         {
30825                             tag : 'div',
30826                             cls : 'roo-upload-cropbox-empty-notify',
30827                             html : this.emptyText
30828                         },
30829                         {
30830                             tag : 'div',
30831                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30832                             html : this.rotateNotify
30833                         }
30834                     ]
30835                 },
30836                 {
30837                     tag : 'div',
30838                     cls : 'roo-upload-cropbox-footer',
30839                     cn : {
30840                         tag : 'div',
30841                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30842                         cn : []
30843                     }
30844                 }
30845             ]
30846         };
30847         
30848         return cfg;
30849     },
30850     
30851     onRender : function(ct, position)
30852     {
30853         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30854         
30855         if (this.buttons.length) {
30856             
30857             Roo.each(this.buttons, function(bb) {
30858                 
30859                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30860                 
30861                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30862                 
30863             }, this);
30864         }
30865         
30866         if(this.loadMask){
30867             this.maskEl = this.el;
30868         }
30869     },
30870     
30871     initEvents : function()
30872     {
30873         this.urlAPI = (window.createObjectURL && window) || 
30874                                 (window.URL && URL.revokeObjectURL && URL) || 
30875                                 (window.webkitURL && webkitURL);
30876                         
30877         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30878         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30879         
30880         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30881         this.selectorEl.hide();
30882         
30883         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30884         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30885         
30886         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30887         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30888         this.thumbEl.hide();
30889         
30890         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30891         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30892         
30893         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30894         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30895         this.errorEl.hide();
30896         
30897         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30898         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30899         this.footerEl.hide();
30900         
30901         this.setThumbBoxSize();
30902         
30903         this.bind();
30904         
30905         this.resize();
30906         
30907         this.fireEvent('initial', this);
30908     },
30909
30910     bind : function()
30911     {
30912         var _this = this;
30913         
30914         window.addEventListener("resize", function() { _this.resize(); } );
30915         
30916         this.bodyEl.on('click', this.beforeSelectFile, this);
30917         
30918         if(Roo.isTouch){
30919             this.bodyEl.on('touchstart', this.onTouchStart, this);
30920             this.bodyEl.on('touchmove', this.onTouchMove, this);
30921             this.bodyEl.on('touchend', this.onTouchEnd, this);
30922         }
30923         
30924         if(!Roo.isTouch){
30925             this.bodyEl.on('mousedown', this.onMouseDown, this);
30926             this.bodyEl.on('mousemove', this.onMouseMove, this);
30927             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30928             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30929             Roo.get(document).on('mouseup', this.onMouseUp, this);
30930         }
30931         
30932         this.selectorEl.on('change', this.onFileSelected, this);
30933     },
30934     
30935     reset : function()
30936     {    
30937         this.scale = 0;
30938         this.baseScale = 1;
30939         this.rotate = 0;
30940         this.baseRotate = 1;
30941         this.dragable = false;
30942         this.pinching = false;
30943         this.mouseX = 0;
30944         this.mouseY = 0;
30945         this.cropData = false;
30946         this.notifyEl.dom.innerHTML = this.emptyText;
30947         
30948         this.selectorEl.dom.value = '';
30949         
30950     },
30951     
30952     resize : function()
30953     {
30954         if(this.fireEvent('resize', this) != false){
30955             this.setThumbBoxPosition();
30956             this.setCanvasPosition();
30957         }
30958     },
30959     
30960     onFooterButtonClick : function(e, el, o, type)
30961     {
30962         switch (type) {
30963             case 'rotate-left' :
30964                 this.onRotateLeft(e);
30965                 break;
30966             case 'rotate-right' :
30967                 this.onRotateRight(e);
30968                 break;
30969             case 'picture' :
30970                 this.beforeSelectFile(e);
30971                 break;
30972             case 'trash' :
30973                 this.trash(e);
30974                 break;
30975             case 'crop' :
30976                 this.crop(e);
30977                 break;
30978             case 'download' :
30979                 this.download(e);
30980                 break;
30981             default :
30982                 break;
30983         }
30984         
30985         this.fireEvent('footerbuttonclick', this, type);
30986     },
30987     
30988     beforeSelectFile : function(e)
30989     {
30990         e.preventDefault();
30991         
30992         if(this.fireEvent('beforeselectfile', this) != false){
30993             this.selectorEl.dom.click();
30994         }
30995     },
30996     
30997     onFileSelected : function(e)
30998     {
30999         e.preventDefault();
31000         
31001         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31002             return;
31003         }
31004         
31005         var file = this.selectorEl.dom.files[0];
31006         
31007         if(this.fireEvent('inspect', this, file) != false){
31008             this.prepare(file);
31009         }
31010         
31011     },
31012     
31013     trash : function(e)
31014     {
31015         this.fireEvent('trash', this);
31016     },
31017     
31018     download : function(e)
31019     {
31020         this.fireEvent('download', this);
31021     },
31022     
31023     loadCanvas : function(src)
31024     {   
31025         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31026             
31027             this.reset();
31028             
31029             this.imageEl = document.createElement('img');
31030             
31031             var _this = this;
31032             
31033             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31034             
31035             this.imageEl.src = src;
31036         }
31037     },
31038     
31039     onLoadCanvas : function()
31040     {   
31041         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31042         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31043         
31044         this.bodyEl.un('click', this.beforeSelectFile, this);
31045         
31046         this.notifyEl.hide();
31047         this.thumbEl.show();
31048         this.footerEl.show();
31049         
31050         this.baseRotateLevel();
31051         
31052         if(this.isDocument){
31053             this.setThumbBoxSize();
31054         }
31055         
31056         this.setThumbBoxPosition();
31057         
31058         this.baseScaleLevel();
31059         
31060         this.draw();
31061         
31062         this.resize();
31063         
31064         this.canvasLoaded = true;
31065         
31066         if(this.loadMask){
31067             this.maskEl.unmask();
31068         }
31069         
31070     },
31071     
31072     setCanvasPosition : function()
31073     {   
31074         if(!this.canvasEl){
31075             return;
31076         }
31077         
31078         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31079         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31080         
31081         this.previewEl.setLeft(pw);
31082         this.previewEl.setTop(ph);
31083         
31084     },
31085     
31086     onMouseDown : function(e)
31087     {   
31088         e.stopEvent();
31089         
31090         this.dragable = true;
31091         this.pinching = false;
31092         
31093         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31094             this.dragable = false;
31095             return;
31096         }
31097         
31098         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31099         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31100         
31101     },
31102     
31103     onMouseMove : function(e)
31104     {   
31105         e.stopEvent();
31106         
31107         if(!this.canvasLoaded){
31108             return;
31109         }
31110         
31111         if (!this.dragable){
31112             return;
31113         }
31114         
31115         var minX = Math.ceil(this.thumbEl.getLeft(true));
31116         var minY = Math.ceil(this.thumbEl.getTop(true));
31117         
31118         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31119         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31120         
31121         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31122         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31123         
31124         x = x - this.mouseX;
31125         y = y - this.mouseY;
31126         
31127         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31128         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31129         
31130         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31131         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31132         
31133         this.previewEl.setLeft(bgX);
31134         this.previewEl.setTop(bgY);
31135         
31136         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31137         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31138     },
31139     
31140     onMouseUp : function(e)
31141     {   
31142         e.stopEvent();
31143         
31144         this.dragable = false;
31145     },
31146     
31147     onMouseWheel : function(e)
31148     {   
31149         e.stopEvent();
31150         
31151         this.startScale = this.scale;
31152         
31153         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31154         
31155         if(!this.zoomable()){
31156             this.scale = this.startScale;
31157             return;
31158         }
31159         
31160         this.draw();
31161         
31162         return;
31163     },
31164     
31165     zoomable : function()
31166     {
31167         var minScale = this.thumbEl.getWidth() / this.minWidth;
31168         
31169         if(this.minWidth < this.minHeight){
31170             minScale = this.thumbEl.getHeight() / this.minHeight;
31171         }
31172         
31173         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31174         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31175         
31176         if(
31177                 this.isDocument &&
31178                 (this.rotate == 0 || this.rotate == 180) && 
31179                 (
31180                     width > this.imageEl.OriginWidth || 
31181                     height > this.imageEl.OriginHeight ||
31182                     (width < this.minWidth && height < this.minHeight)
31183                 )
31184         ){
31185             return false;
31186         }
31187         
31188         if(
31189                 this.isDocument &&
31190                 (this.rotate == 90 || this.rotate == 270) && 
31191                 (
31192                     width > this.imageEl.OriginWidth || 
31193                     height > this.imageEl.OriginHeight ||
31194                     (width < this.minHeight && height < this.minWidth)
31195                 )
31196         ){
31197             return false;
31198         }
31199         
31200         if(
31201                 !this.isDocument &&
31202                 (this.rotate == 0 || this.rotate == 180) && 
31203                 (
31204                     width < this.minWidth || 
31205                     width > this.imageEl.OriginWidth || 
31206                     height < this.minHeight || 
31207                     height > this.imageEl.OriginHeight
31208                 )
31209         ){
31210             return false;
31211         }
31212         
31213         if(
31214                 !this.isDocument &&
31215                 (this.rotate == 90 || this.rotate == 270) && 
31216                 (
31217                     width < this.minHeight || 
31218                     width > this.imageEl.OriginWidth || 
31219                     height < this.minWidth || 
31220                     height > this.imageEl.OriginHeight
31221                 )
31222         ){
31223             return false;
31224         }
31225         
31226         return true;
31227         
31228     },
31229     
31230     onRotateLeft : function(e)
31231     {   
31232         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31233             
31234             var minScale = this.thumbEl.getWidth() / this.minWidth;
31235             
31236             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31237             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31238             
31239             this.startScale = this.scale;
31240             
31241             while (this.getScaleLevel() < minScale){
31242             
31243                 this.scale = this.scale + 1;
31244                 
31245                 if(!this.zoomable()){
31246                     break;
31247                 }
31248                 
31249                 if(
31250                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31251                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31252                 ){
31253                     continue;
31254                 }
31255                 
31256                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31257
31258                 this.draw();
31259                 
31260                 return;
31261             }
31262             
31263             this.scale = this.startScale;
31264             
31265             this.onRotateFail();
31266             
31267             return false;
31268         }
31269         
31270         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31271
31272         if(this.isDocument){
31273             this.setThumbBoxSize();
31274             this.setThumbBoxPosition();
31275             this.setCanvasPosition();
31276         }
31277         
31278         this.draw();
31279         
31280         this.fireEvent('rotate', this, 'left');
31281         
31282     },
31283     
31284     onRotateRight : function(e)
31285     {
31286         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31287             
31288             var minScale = this.thumbEl.getWidth() / this.minWidth;
31289         
31290             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31291             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31292             
31293             this.startScale = this.scale;
31294             
31295             while (this.getScaleLevel() < minScale){
31296             
31297                 this.scale = this.scale + 1;
31298                 
31299                 if(!this.zoomable()){
31300                     break;
31301                 }
31302                 
31303                 if(
31304                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31305                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31306                 ){
31307                     continue;
31308                 }
31309                 
31310                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31311
31312                 this.draw();
31313                 
31314                 return;
31315             }
31316             
31317             this.scale = this.startScale;
31318             
31319             this.onRotateFail();
31320             
31321             return false;
31322         }
31323         
31324         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31325
31326         if(this.isDocument){
31327             this.setThumbBoxSize();
31328             this.setThumbBoxPosition();
31329             this.setCanvasPosition();
31330         }
31331         
31332         this.draw();
31333         
31334         this.fireEvent('rotate', this, 'right');
31335     },
31336     
31337     onRotateFail : function()
31338     {
31339         this.errorEl.show(true);
31340         
31341         var _this = this;
31342         
31343         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31344     },
31345     
31346     draw : function()
31347     {
31348         this.previewEl.dom.innerHTML = '';
31349         
31350         var canvasEl = document.createElement("canvas");
31351         
31352         var contextEl = canvasEl.getContext("2d");
31353         
31354         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31355         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31356         var center = this.imageEl.OriginWidth / 2;
31357         
31358         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31359             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31360             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31361             center = this.imageEl.OriginHeight / 2;
31362         }
31363         
31364         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31365         
31366         contextEl.translate(center, center);
31367         contextEl.rotate(this.rotate * Math.PI / 180);
31368
31369         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31370         
31371         this.canvasEl = document.createElement("canvas");
31372         
31373         this.contextEl = this.canvasEl.getContext("2d");
31374         
31375         switch (this.rotate) {
31376             case 0 :
31377                 
31378                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31379                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31380                 
31381                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31382                 
31383                 break;
31384             case 90 : 
31385                 
31386                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31387                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31388                 
31389                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31390                     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);
31391                     break;
31392                 }
31393                 
31394                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31395                 
31396                 break;
31397             case 180 :
31398                 
31399                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31400                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31401                 
31402                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31403                     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);
31404                     break;
31405                 }
31406                 
31407                 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);
31408                 
31409                 break;
31410             case 270 :
31411                 
31412                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31413                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31414         
31415                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31416                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31417                     break;
31418                 }
31419                 
31420                 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);
31421                 
31422                 break;
31423             default : 
31424                 break;
31425         }
31426         
31427         this.previewEl.appendChild(this.canvasEl);
31428         
31429         this.setCanvasPosition();
31430     },
31431     
31432     crop : function()
31433     {
31434         if(!this.canvasLoaded){
31435             return;
31436         }
31437         
31438         var imageCanvas = document.createElement("canvas");
31439         
31440         var imageContext = imageCanvas.getContext("2d");
31441         
31442         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31443         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31444         
31445         var center = imageCanvas.width / 2;
31446         
31447         imageContext.translate(center, center);
31448         
31449         imageContext.rotate(this.rotate * Math.PI / 180);
31450         
31451         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31452         
31453         var canvas = document.createElement("canvas");
31454         
31455         var context = canvas.getContext("2d");
31456                 
31457         canvas.width = this.minWidth;
31458         canvas.height = this.minHeight;
31459
31460         switch (this.rotate) {
31461             case 0 :
31462                 
31463                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31464                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31465                 
31466                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31467                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31468                 
31469                 var targetWidth = this.minWidth - 2 * x;
31470                 var targetHeight = this.minHeight - 2 * y;
31471                 
31472                 var scale = 1;
31473                 
31474                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31475                     scale = targetWidth / width;
31476                 }
31477                 
31478                 if(x > 0 && y == 0){
31479                     scale = targetHeight / height;
31480                 }
31481                 
31482                 if(x > 0 && y > 0){
31483                     scale = targetWidth / width;
31484                     
31485                     if(width < height){
31486                         scale = targetHeight / height;
31487                     }
31488                 }
31489                 
31490                 context.scale(scale, scale);
31491                 
31492                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31493                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31494
31495                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31496                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31497
31498                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31499                 
31500                 break;
31501             case 90 : 
31502                 
31503                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31504                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31505                 
31506                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31507                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31508                 
31509                 var targetWidth = this.minWidth - 2 * x;
31510                 var targetHeight = this.minHeight - 2 * y;
31511                 
31512                 var scale = 1;
31513                 
31514                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31515                     scale = targetWidth / width;
31516                 }
31517                 
31518                 if(x > 0 && y == 0){
31519                     scale = targetHeight / height;
31520                 }
31521                 
31522                 if(x > 0 && y > 0){
31523                     scale = targetWidth / width;
31524                     
31525                     if(width < height){
31526                         scale = targetHeight / height;
31527                     }
31528                 }
31529                 
31530                 context.scale(scale, scale);
31531                 
31532                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31533                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31534
31535                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31536                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31537                 
31538                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31539                 
31540                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31541                 
31542                 break;
31543             case 180 :
31544                 
31545                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31546                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31547                 
31548                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31549                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31550                 
31551                 var targetWidth = this.minWidth - 2 * x;
31552                 var targetHeight = this.minHeight - 2 * y;
31553                 
31554                 var scale = 1;
31555                 
31556                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31557                     scale = targetWidth / width;
31558                 }
31559                 
31560                 if(x > 0 && y == 0){
31561                     scale = targetHeight / height;
31562                 }
31563                 
31564                 if(x > 0 && y > 0){
31565                     scale = targetWidth / width;
31566                     
31567                     if(width < height){
31568                         scale = targetHeight / height;
31569                     }
31570                 }
31571                 
31572                 context.scale(scale, scale);
31573                 
31574                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31575                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31576
31577                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31578                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31579
31580                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31581                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31582                 
31583                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31584                 
31585                 break;
31586             case 270 :
31587                 
31588                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31589                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31590                 
31591                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31592                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31593                 
31594                 var targetWidth = this.minWidth - 2 * x;
31595                 var targetHeight = this.minHeight - 2 * y;
31596                 
31597                 var scale = 1;
31598                 
31599                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31600                     scale = targetWidth / width;
31601                 }
31602                 
31603                 if(x > 0 && y == 0){
31604                     scale = targetHeight / height;
31605                 }
31606                 
31607                 if(x > 0 && y > 0){
31608                     scale = targetWidth / width;
31609                     
31610                     if(width < height){
31611                         scale = targetHeight / height;
31612                     }
31613                 }
31614                 
31615                 context.scale(scale, scale);
31616                 
31617                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31618                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31619
31620                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31621                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31622                 
31623                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31624                 
31625                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31626                 
31627                 break;
31628             default : 
31629                 break;
31630         }
31631         
31632         this.cropData = canvas.toDataURL(this.cropType);
31633         
31634         if(this.fireEvent('crop', this, this.cropData) !== false){
31635             this.process(this.file, this.cropData);
31636         }
31637         
31638         return;
31639         
31640     },
31641     
31642     setThumbBoxSize : function()
31643     {
31644         var width, height;
31645         
31646         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31647             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31648             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31649             
31650             this.minWidth = width;
31651             this.minHeight = height;
31652             
31653             if(this.rotate == 90 || this.rotate == 270){
31654                 this.minWidth = height;
31655                 this.minHeight = width;
31656             }
31657         }
31658         
31659         height = 300;
31660         width = Math.ceil(this.minWidth * height / this.minHeight);
31661         
31662         if(this.minWidth > this.minHeight){
31663             width = 300;
31664             height = Math.ceil(this.minHeight * width / this.minWidth);
31665         }
31666         
31667         this.thumbEl.setStyle({
31668             width : width + 'px',
31669             height : height + 'px'
31670         });
31671
31672         return;
31673             
31674     },
31675     
31676     setThumbBoxPosition : function()
31677     {
31678         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31679         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31680         
31681         this.thumbEl.setLeft(x);
31682         this.thumbEl.setTop(y);
31683         
31684     },
31685     
31686     baseRotateLevel : function()
31687     {
31688         this.baseRotate = 1;
31689         
31690         if(
31691                 typeof(this.exif) != 'undefined' &&
31692                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31693                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31694         ){
31695             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31696         }
31697         
31698         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31699         
31700     },
31701     
31702     baseScaleLevel : function()
31703     {
31704         var width, height;
31705         
31706         if(this.isDocument){
31707             
31708             if(this.baseRotate == 6 || this.baseRotate == 8){
31709             
31710                 height = this.thumbEl.getHeight();
31711                 this.baseScale = height / this.imageEl.OriginWidth;
31712
31713                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31714                     width = this.thumbEl.getWidth();
31715                     this.baseScale = width / this.imageEl.OriginHeight;
31716                 }
31717
31718                 return;
31719             }
31720
31721             height = this.thumbEl.getHeight();
31722             this.baseScale = height / this.imageEl.OriginHeight;
31723
31724             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31725                 width = this.thumbEl.getWidth();
31726                 this.baseScale = width / this.imageEl.OriginWidth;
31727             }
31728
31729             return;
31730         }
31731         
31732         if(this.baseRotate == 6 || this.baseRotate == 8){
31733             
31734             width = this.thumbEl.getHeight();
31735             this.baseScale = width / this.imageEl.OriginHeight;
31736             
31737             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31738                 height = this.thumbEl.getWidth();
31739                 this.baseScale = height / this.imageEl.OriginHeight;
31740             }
31741             
31742             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31743                 height = this.thumbEl.getWidth();
31744                 this.baseScale = height / this.imageEl.OriginHeight;
31745                 
31746                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31747                     width = this.thumbEl.getHeight();
31748                     this.baseScale = width / this.imageEl.OriginWidth;
31749                 }
31750             }
31751             
31752             return;
31753         }
31754         
31755         width = this.thumbEl.getWidth();
31756         this.baseScale = width / this.imageEl.OriginWidth;
31757         
31758         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31759             height = this.thumbEl.getHeight();
31760             this.baseScale = height / this.imageEl.OriginHeight;
31761         }
31762         
31763         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31764             
31765             height = this.thumbEl.getHeight();
31766             this.baseScale = height / this.imageEl.OriginHeight;
31767             
31768             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31769                 width = this.thumbEl.getWidth();
31770                 this.baseScale = width / this.imageEl.OriginWidth;
31771             }
31772             
31773         }
31774         
31775         return;
31776     },
31777     
31778     getScaleLevel : function()
31779     {
31780         return this.baseScale * Math.pow(1.1, this.scale);
31781     },
31782     
31783     onTouchStart : function(e)
31784     {
31785         if(!this.canvasLoaded){
31786             this.beforeSelectFile(e);
31787             return;
31788         }
31789         
31790         var touches = e.browserEvent.touches;
31791         
31792         if(!touches){
31793             return;
31794         }
31795         
31796         if(touches.length == 1){
31797             this.onMouseDown(e);
31798             return;
31799         }
31800         
31801         if(touches.length != 2){
31802             return;
31803         }
31804         
31805         var coords = [];
31806         
31807         for(var i = 0, finger; finger = touches[i]; i++){
31808             coords.push(finger.pageX, finger.pageY);
31809         }
31810         
31811         var x = Math.pow(coords[0] - coords[2], 2);
31812         var y = Math.pow(coords[1] - coords[3], 2);
31813         
31814         this.startDistance = Math.sqrt(x + y);
31815         
31816         this.startScale = this.scale;
31817         
31818         this.pinching = true;
31819         this.dragable = false;
31820         
31821     },
31822     
31823     onTouchMove : function(e)
31824     {
31825         if(!this.pinching && !this.dragable){
31826             return;
31827         }
31828         
31829         var touches = e.browserEvent.touches;
31830         
31831         if(!touches){
31832             return;
31833         }
31834         
31835         if(this.dragable){
31836             this.onMouseMove(e);
31837             return;
31838         }
31839         
31840         var coords = [];
31841         
31842         for(var i = 0, finger; finger = touches[i]; i++){
31843             coords.push(finger.pageX, finger.pageY);
31844         }
31845         
31846         var x = Math.pow(coords[0] - coords[2], 2);
31847         var y = Math.pow(coords[1] - coords[3], 2);
31848         
31849         this.endDistance = Math.sqrt(x + y);
31850         
31851         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31852         
31853         if(!this.zoomable()){
31854             this.scale = this.startScale;
31855             return;
31856         }
31857         
31858         this.draw();
31859         
31860     },
31861     
31862     onTouchEnd : function(e)
31863     {
31864         this.pinching = false;
31865         this.dragable = false;
31866         
31867     },
31868     
31869     process : function(file, crop)
31870     {
31871         if(this.loadMask){
31872             this.maskEl.mask(this.loadingText);
31873         }
31874         
31875         this.xhr = new XMLHttpRequest();
31876         
31877         file.xhr = this.xhr;
31878
31879         this.xhr.open(this.method, this.url, true);
31880         
31881         var headers = {
31882             "Accept": "application/json",
31883             "Cache-Control": "no-cache",
31884             "X-Requested-With": "XMLHttpRequest"
31885         };
31886         
31887         for (var headerName in headers) {
31888             var headerValue = headers[headerName];
31889             if (headerValue) {
31890                 this.xhr.setRequestHeader(headerName, headerValue);
31891             }
31892         }
31893         
31894         var _this = this;
31895         
31896         this.xhr.onload = function()
31897         {
31898             _this.xhrOnLoad(_this.xhr);
31899         }
31900         
31901         this.xhr.onerror = function()
31902         {
31903             _this.xhrOnError(_this.xhr);
31904         }
31905         
31906         var formData = new FormData();
31907
31908         formData.append('returnHTML', 'NO');
31909         
31910         if(crop){
31911             formData.append('crop', crop);
31912         }
31913         
31914         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31915             formData.append(this.paramName, file, file.name);
31916         }
31917         
31918         if(typeof(file.filename) != 'undefined'){
31919             formData.append('filename', file.filename);
31920         }
31921         
31922         if(typeof(file.mimetype) != 'undefined'){
31923             formData.append('mimetype', file.mimetype);
31924         }
31925         
31926         if(this.fireEvent('arrange', this, formData) != false){
31927             this.xhr.send(formData);
31928         };
31929     },
31930     
31931     xhrOnLoad : function(xhr)
31932     {
31933         if(this.loadMask){
31934             this.maskEl.unmask();
31935         }
31936         
31937         if (xhr.readyState !== 4) {
31938             this.fireEvent('exception', this, xhr);
31939             return;
31940         }
31941
31942         var response = Roo.decode(xhr.responseText);
31943         
31944         if(!response.success){
31945             this.fireEvent('exception', this, xhr);
31946             return;
31947         }
31948         
31949         var response = Roo.decode(xhr.responseText);
31950         
31951         this.fireEvent('upload', this, response);
31952         
31953     },
31954     
31955     xhrOnError : function()
31956     {
31957         if(this.loadMask){
31958             this.maskEl.unmask();
31959         }
31960         
31961         Roo.log('xhr on error');
31962         
31963         var response = Roo.decode(xhr.responseText);
31964           
31965         Roo.log(response);
31966         
31967     },
31968     
31969     prepare : function(file)
31970     {   
31971         if(this.loadMask){
31972             this.maskEl.mask(this.loadingText);
31973         }
31974         
31975         this.file = false;
31976         this.exif = {};
31977         
31978         if(typeof(file) === 'string'){
31979             this.loadCanvas(file);
31980             return;
31981         }
31982         
31983         if(!file || !this.urlAPI){
31984             return;
31985         }
31986         
31987         this.file = file;
31988         this.cropType = file.type;
31989         
31990         var _this = this;
31991         
31992         if(this.fireEvent('prepare', this, this.file) != false){
31993             
31994             var reader = new FileReader();
31995             
31996             reader.onload = function (e) {
31997                 if (e.target.error) {
31998                     Roo.log(e.target.error);
31999                     return;
32000                 }
32001                 
32002                 var buffer = e.target.result,
32003                     dataView = new DataView(buffer),
32004                     offset = 2,
32005                     maxOffset = dataView.byteLength - 4,
32006                     markerBytes,
32007                     markerLength;
32008                 
32009                 if (dataView.getUint16(0) === 0xffd8) {
32010                     while (offset < maxOffset) {
32011                         markerBytes = dataView.getUint16(offset);
32012                         
32013                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32014                             markerLength = dataView.getUint16(offset + 2) + 2;
32015                             if (offset + markerLength > dataView.byteLength) {
32016                                 Roo.log('Invalid meta data: Invalid segment size.');
32017                                 break;
32018                             }
32019                             
32020                             if(markerBytes == 0xffe1){
32021                                 _this.parseExifData(
32022                                     dataView,
32023                                     offset,
32024                                     markerLength
32025                                 );
32026                             }
32027                             
32028                             offset += markerLength;
32029                             
32030                             continue;
32031                         }
32032                         
32033                         break;
32034                     }
32035                     
32036                 }
32037                 
32038                 var url = _this.urlAPI.createObjectURL(_this.file);
32039                 
32040                 _this.loadCanvas(url);
32041                 
32042                 return;
32043             }
32044             
32045             reader.readAsArrayBuffer(this.file);
32046             
32047         }
32048         
32049     },
32050     
32051     parseExifData : function(dataView, offset, length)
32052     {
32053         var tiffOffset = offset + 10,
32054             littleEndian,
32055             dirOffset;
32056     
32057         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32058             // No Exif data, might be XMP data instead
32059             return;
32060         }
32061         
32062         // Check for the ASCII code for "Exif" (0x45786966):
32063         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32064             // No Exif data, might be XMP data instead
32065             return;
32066         }
32067         if (tiffOffset + 8 > dataView.byteLength) {
32068             Roo.log('Invalid Exif data: Invalid segment size.');
32069             return;
32070         }
32071         // Check for the two null bytes:
32072         if (dataView.getUint16(offset + 8) !== 0x0000) {
32073             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32074             return;
32075         }
32076         // Check the byte alignment:
32077         switch (dataView.getUint16(tiffOffset)) {
32078         case 0x4949:
32079             littleEndian = true;
32080             break;
32081         case 0x4D4D:
32082             littleEndian = false;
32083             break;
32084         default:
32085             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32086             return;
32087         }
32088         // Check for the TIFF tag marker (0x002A):
32089         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32090             Roo.log('Invalid Exif data: Missing TIFF marker.');
32091             return;
32092         }
32093         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32094         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32095         
32096         this.parseExifTags(
32097             dataView,
32098             tiffOffset,
32099             tiffOffset + dirOffset,
32100             littleEndian
32101         );
32102     },
32103     
32104     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32105     {
32106         var tagsNumber,
32107             dirEndOffset,
32108             i;
32109         if (dirOffset + 6 > dataView.byteLength) {
32110             Roo.log('Invalid Exif data: Invalid directory offset.');
32111             return;
32112         }
32113         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32114         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32115         if (dirEndOffset + 4 > dataView.byteLength) {
32116             Roo.log('Invalid Exif data: Invalid directory size.');
32117             return;
32118         }
32119         for (i = 0; i < tagsNumber; i += 1) {
32120             this.parseExifTag(
32121                 dataView,
32122                 tiffOffset,
32123                 dirOffset + 2 + 12 * i, // tag offset
32124                 littleEndian
32125             );
32126         }
32127         // Return the offset to the next directory:
32128         return dataView.getUint32(dirEndOffset, littleEndian);
32129     },
32130     
32131     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32132     {
32133         var tag = dataView.getUint16(offset, littleEndian);
32134         
32135         this.exif[tag] = this.getExifValue(
32136             dataView,
32137             tiffOffset,
32138             offset,
32139             dataView.getUint16(offset + 2, littleEndian), // tag type
32140             dataView.getUint32(offset + 4, littleEndian), // tag length
32141             littleEndian
32142         );
32143     },
32144     
32145     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32146     {
32147         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32148             tagSize,
32149             dataOffset,
32150             values,
32151             i,
32152             str,
32153             c;
32154     
32155         if (!tagType) {
32156             Roo.log('Invalid Exif data: Invalid tag type.');
32157             return;
32158         }
32159         
32160         tagSize = tagType.size * length;
32161         // Determine if the value is contained in the dataOffset bytes,
32162         // or if the value at the dataOffset is a pointer to the actual data:
32163         dataOffset = tagSize > 4 ?
32164                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32165         if (dataOffset + tagSize > dataView.byteLength) {
32166             Roo.log('Invalid Exif data: Invalid data offset.');
32167             return;
32168         }
32169         if (length === 1) {
32170             return tagType.getValue(dataView, dataOffset, littleEndian);
32171         }
32172         values = [];
32173         for (i = 0; i < length; i += 1) {
32174             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32175         }
32176         
32177         if (tagType.ascii) {
32178             str = '';
32179             // Concatenate the chars:
32180             for (i = 0; i < values.length; i += 1) {
32181                 c = values[i];
32182                 // Ignore the terminating NULL byte(s):
32183                 if (c === '\u0000') {
32184                     break;
32185                 }
32186                 str += c;
32187             }
32188             return str;
32189         }
32190         return values;
32191     }
32192     
32193 });
32194
32195 Roo.apply(Roo.bootstrap.UploadCropbox, {
32196     tags : {
32197         'Orientation': 0x0112
32198     },
32199     
32200     Orientation: {
32201             1: 0, //'top-left',
32202 //            2: 'top-right',
32203             3: 180, //'bottom-right',
32204 //            4: 'bottom-left',
32205 //            5: 'left-top',
32206             6: 90, //'right-top',
32207 //            7: 'right-bottom',
32208             8: 270 //'left-bottom'
32209     },
32210     
32211     exifTagTypes : {
32212         // byte, 8-bit unsigned int:
32213         1: {
32214             getValue: function (dataView, dataOffset) {
32215                 return dataView.getUint8(dataOffset);
32216             },
32217             size: 1
32218         },
32219         // ascii, 8-bit byte:
32220         2: {
32221             getValue: function (dataView, dataOffset) {
32222                 return String.fromCharCode(dataView.getUint8(dataOffset));
32223             },
32224             size: 1,
32225             ascii: true
32226         },
32227         // short, 16 bit int:
32228         3: {
32229             getValue: function (dataView, dataOffset, littleEndian) {
32230                 return dataView.getUint16(dataOffset, littleEndian);
32231             },
32232             size: 2
32233         },
32234         // long, 32 bit int:
32235         4: {
32236             getValue: function (dataView, dataOffset, littleEndian) {
32237                 return dataView.getUint32(dataOffset, littleEndian);
32238             },
32239             size: 4
32240         },
32241         // rational = two long values, first is numerator, second is denominator:
32242         5: {
32243             getValue: function (dataView, dataOffset, littleEndian) {
32244                 return dataView.getUint32(dataOffset, littleEndian) /
32245                     dataView.getUint32(dataOffset + 4, littleEndian);
32246             },
32247             size: 8
32248         },
32249         // slong, 32 bit signed int:
32250         9: {
32251             getValue: function (dataView, dataOffset, littleEndian) {
32252                 return dataView.getInt32(dataOffset, littleEndian);
32253             },
32254             size: 4
32255         },
32256         // srational, two slongs, first is numerator, second is denominator:
32257         10: {
32258             getValue: function (dataView, dataOffset, littleEndian) {
32259                 return dataView.getInt32(dataOffset, littleEndian) /
32260                     dataView.getInt32(dataOffset + 4, littleEndian);
32261             },
32262             size: 8
32263         }
32264     },
32265     
32266     footer : {
32267         STANDARD : [
32268             {
32269                 tag : 'div',
32270                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32271                 action : 'rotate-left',
32272                 cn : [
32273                     {
32274                         tag : 'button',
32275                         cls : 'btn btn-default',
32276                         html : '<i class="fa fa-undo"></i>'
32277                     }
32278                 ]
32279             },
32280             {
32281                 tag : 'div',
32282                 cls : 'btn-group roo-upload-cropbox-picture',
32283                 action : 'picture',
32284                 cn : [
32285                     {
32286                         tag : 'button',
32287                         cls : 'btn btn-default',
32288                         html : '<i class="fa fa-picture-o"></i>'
32289                     }
32290                 ]
32291             },
32292             {
32293                 tag : 'div',
32294                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32295                 action : 'rotate-right',
32296                 cn : [
32297                     {
32298                         tag : 'button',
32299                         cls : 'btn btn-default',
32300                         html : '<i class="fa fa-repeat"></i>'
32301                     }
32302                 ]
32303             }
32304         ],
32305         DOCUMENT : [
32306             {
32307                 tag : 'div',
32308                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32309                 action : 'rotate-left',
32310                 cn : [
32311                     {
32312                         tag : 'button',
32313                         cls : 'btn btn-default',
32314                         html : '<i class="fa fa-undo"></i>'
32315                     }
32316                 ]
32317             },
32318             {
32319                 tag : 'div',
32320                 cls : 'btn-group roo-upload-cropbox-download',
32321                 action : 'download',
32322                 cn : [
32323                     {
32324                         tag : 'button',
32325                         cls : 'btn btn-default',
32326                         html : '<i class="fa fa-download"></i>'
32327                     }
32328                 ]
32329             },
32330             {
32331                 tag : 'div',
32332                 cls : 'btn-group roo-upload-cropbox-crop',
32333                 action : 'crop',
32334                 cn : [
32335                     {
32336                         tag : 'button',
32337                         cls : 'btn btn-default',
32338                         html : '<i class="fa fa-crop"></i>'
32339                     }
32340                 ]
32341             },
32342             {
32343                 tag : 'div',
32344                 cls : 'btn-group roo-upload-cropbox-trash',
32345                 action : 'trash',
32346                 cn : [
32347                     {
32348                         tag : 'button',
32349                         cls : 'btn btn-default',
32350                         html : '<i class="fa fa-trash"></i>'
32351                     }
32352                 ]
32353             },
32354             {
32355                 tag : 'div',
32356                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32357                 action : 'rotate-right',
32358                 cn : [
32359                     {
32360                         tag : 'button',
32361                         cls : 'btn btn-default',
32362                         html : '<i class="fa fa-repeat"></i>'
32363                     }
32364                 ]
32365             }
32366         ],
32367         ROTATOR : [
32368             {
32369                 tag : 'div',
32370                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32371                 action : 'rotate-left',
32372                 cn : [
32373                     {
32374                         tag : 'button',
32375                         cls : 'btn btn-default',
32376                         html : '<i class="fa fa-undo"></i>'
32377                     }
32378                 ]
32379             },
32380             {
32381                 tag : 'div',
32382                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32383                 action : 'rotate-right',
32384                 cn : [
32385                     {
32386                         tag : 'button',
32387                         cls : 'btn btn-default',
32388                         html : '<i class="fa fa-repeat"></i>'
32389                     }
32390                 ]
32391             }
32392         ]
32393     }
32394 });
32395
32396 /*
32397 * Licence: LGPL
32398 */
32399
32400 /**
32401  * @class Roo.bootstrap.DocumentManager
32402  * @extends Roo.bootstrap.Component
32403  * Bootstrap DocumentManager class
32404  * @cfg {String} paramName default 'imageUpload'
32405  * @cfg {String} toolTipName default 'filename'
32406  * @cfg {String} method default POST
32407  * @cfg {String} url action url
32408  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32409  * @cfg {Boolean} multiple multiple upload default true
32410  * @cfg {Number} thumbSize default 300
32411  * @cfg {String} fieldLabel
32412  * @cfg {Number} labelWidth default 4
32413  * @cfg {String} labelAlign (left|top) default left
32414  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32415 * @cfg {Number} labellg set the width of label (1-12)
32416  * @cfg {Number} labelmd set the width of label (1-12)
32417  * @cfg {Number} labelsm set the width of label (1-12)
32418  * @cfg {Number} labelxs set the width of label (1-12)
32419  * 
32420  * @constructor
32421  * Create a new DocumentManager
32422  * @param {Object} config The config object
32423  */
32424
32425 Roo.bootstrap.DocumentManager = function(config){
32426     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32427     
32428     this.files = [];
32429     this.delegates = [];
32430     
32431     this.addEvents({
32432         /**
32433          * @event initial
32434          * Fire when initial the DocumentManager
32435          * @param {Roo.bootstrap.DocumentManager} this
32436          */
32437         "initial" : true,
32438         /**
32439          * @event inspect
32440          * inspect selected file
32441          * @param {Roo.bootstrap.DocumentManager} this
32442          * @param {File} file
32443          */
32444         "inspect" : true,
32445         /**
32446          * @event exception
32447          * Fire when xhr load exception
32448          * @param {Roo.bootstrap.DocumentManager} this
32449          * @param {XMLHttpRequest} xhr
32450          */
32451         "exception" : true,
32452         /**
32453          * @event afterupload
32454          * Fire when xhr load exception
32455          * @param {Roo.bootstrap.DocumentManager} this
32456          * @param {XMLHttpRequest} xhr
32457          */
32458         "afterupload" : true,
32459         /**
32460          * @event prepare
32461          * prepare the form data
32462          * @param {Roo.bootstrap.DocumentManager} this
32463          * @param {Object} formData
32464          */
32465         "prepare" : true,
32466         /**
32467          * @event remove
32468          * Fire when remove the file
32469          * @param {Roo.bootstrap.DocumentManager} this
32470          * @param {Object} file
32471          */
32472         "remove" : true,
32473         /**
32474          * @event refresh
32475          * Fire after refresh the file
32476          * @param {Roo.bootstrap.DocumentManager} this
32477          */
32478         "refresh" : true,
32479         /**
32480          * @event click
32481          * Fire after click the image
32482          * @param {Roo.bootstrap.DocumentManager} this
32483          * @param {Object} file
32484          */
32485         "click" : true,
32486         /**
32487          * @event edit
32488          * Fire when upload a image and editable set to true
32489          * @param {Roo.bootstrap.DocumentManager} this
32490          * @param {Object} file
32491          */
32492         "edit" : true,
32493         /**
32494          * @event beforeselectfile
32495          * Fire before select file
32496          * @param {Roo.bootstrap.DocumentManager} this
32497          */
32498         "beforeselectfile" : true,
32499         /**
32500          * @event process
32501          * Fire before process file
32502          * @param {Roo.bootstrap.DocumentManager} this
32503          * @param {Object} file
32504          */
32505         "process" : true,
32506         /**
32507          * @event previewrendered
32508          * Fire when preview rendered
32509          * @param {Roo.bootstrap.DocumentManager} this
32510          * @param {Object} file
32511          */
32512         "previewrendered" : true,
32513         /**
32514          */
32515         "previewResize" : true
32516         
32517     });
32518 };
32519
32520 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32521     
32522     boxes : 0,
32523     inputName : '',
32524     thumbSize : 300,
32525     multiple : true,
32526     files : false,
32527     method : 'POST',
32528     url : '',
32529     paramName : 'imageUpload',
32530     toolTipName : 'filename',
32531     fieldLabel : '',
32532     labelWidth : 4,
32533     labelAlign : 'left',
32534     editable : true,
32535     delegates : false,
32536     xhr : false, 
32537     
32538     labellg : 0,
32539     labelmd : 0,
32540     labelsm : 0,
32541     labelxs : 0,
32542     
32543     getAutoCreate : function()
32544     {   
32545         var managerWidget = {
32546             tag : 'div',
32547             cls : 'roo-document-manager',
32548             cn : [
32549                 {
32550                     tag : 'input',
32551                     cls : 'roo-document-manager-selector',
32552                     type : 'file'
32553                 },
32554                 {
32555                     tag : 'div',
32556                     cls : 'roo-document-manager-uploader',
32557                     cn : [
32558                         {
32559                             tag : 'div',
32560                             cls : 'roo-document-manager-upload-btn',
32561                             html : '<i class="fa fa-plus"></i>'
32562                         }
32563                     ]
32564                     
32565                 }
32566             ]
32567         };
32568         
32569         var content = [
32570             {
32571                 tag : 'div',
32572                 cls : 'column col-md-12',
32573                 cn : managerWidget
32574             }
32575         ];
32576         
32577         if(this.fieldLabel.length){
32578             
32579             content = [
32580                 {
32581                     tag : 'div',
32582                     cls : 'column col-md-12',
32583                     html : this.fieldLabel
32584                 },
32585                 {
32586                     tag : 'div',
32587                     cls : 'column col-md-12',
32588                     cn : managerWidget
32589                 }
32590             ];
32591
32592             if(this.labelAlign == 'left'){
32593                 content = [
32594                     {
32595                         tag : 'div',
32596                         cls : 'column',
32597                         html : this.fieldLabel
32598                     },
32599                     {
32600                         tag : 'div',
32601                         cls : 'column',
32602                         cn : managerWidget
32603                     }
32604                 ];
32605                 
32606                 if(this.labelWidth > 12){
32607                     content[0].style = "width: " + this.labelWidth + 'px';
32608                 }
32609
32610                 if(this.labelWidth < 13 && this.labelmd == 0){
32611                     this.labelmd = this.labelWidth;
32612                 }
32613
32614                 if(this.labellg > 0){
32615                     content[0].cls += ' col-lg-' + this.labellg;
32616                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32617                 }
32618
32619                 if(this.labelmd > 0){
32620                     content[0].cls += ' col-md-' + this.labelmd;
32621                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32622                 }
32623
32624                 if(this.labelsm > 0){
32625                     content[0].cls += ' col-sm-' + this.labelsm;
32626                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32627                 }
32628
32629                 if(this.labelxs > 0){
32630                     content[0].cls += ' col-xs-' + this.labelxs;
32631                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32632                 }
32633                 
32634             }
32635         }
32636         
32637         var cfg = {
32638             tag : 'div',
32639             cls : 'row clearfix',
32640             cn : content
32641         };
32642         
32643         return cfg;
32644         
32645     },
32646     
32647     initEvents : function()
32648     {
32649         this.managerEl = this.el.select('.roo-document-manager', true).first();
32650         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32651         
32652         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32653         this.selectorEl.hide();
32654         
32655         if(this.multiple){
32656             this.selectorEl.attr('multiple', 'multiple');
32657         }
32658         
32659         this.selectorEl.on('change', this.onFileSelected, this);
32660         
32661         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32662         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32663         
32664         this.uploader.on('click', this.onUploaderClick, this);
32665         
32666         this.renderProgressDialog();
32667         
32668         var _this = this;
32669         
32670         window.addEventListener("resize", function() { _this.refresh(); } );
32671         
32672         this.fireEvent('initial', this);
32673     },
32674     
32675     renderProgressDialog : function()
32676     {
32677         var _this = this;
32678         
32679         this.progressDialog = new Roo.bootstrap.Modal({
32680             cls : 'roo-document-manager-progress-dialog',
32681             allow_close : false,
32682             animate : false,
32683             title : '',
32684             buttons : [
32685                 {
32686                     name  :'cancel',
32687                     weight : 'danger',
32688                     html : 'Cancel'
32689                 }
32690             ], 
32691             listeners : { 
32692                 btnclick : function() {
32693                     _this.uploadCancel();
32694                     this.hide();
32695                 }
32696             }
32697         });
32698          
32699         this.progressDialog.render(Roo.get(document.body));
32700          
32701         this.progress = new Roo.bootstrap.Progress({
32702             cls : 'roo-document-manager-progress',
32703             active : true,
32704             striped : true
32705         });
32706         
32707         this.progress.render(this.progressDialog.getChildContainer());
32708         
32709         this.progressBar = new Roo.bootstrap.ProgressBar({
32710             cls : 'roo-document-manager-progress-bar',
32711             aria_valuenow : 0,
32712             aria_valuemin : 0,
32713             aria_valuemax : 12,
32714             panel : 'success'
32715         });
32716         
32717         this.progressBar.render(this.progress.getChildContainer());
32718     },
32719     
32720     onUploaderClick : function(e)
32721     {
32722         e.preventDefault();
32723      
32724         if(this.fireEvent('beforeselectfile', this) != false){
32725             this.selectorEl.dom.click();
32726         }
32727         
32728     },
32729     
32730     onFileSelected : function(e)
32731     {
32732         e.preventDefault();
32733         
32734         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32735             return;
32736         }
32737         
32738         Roo.each(this.selectorEl.dom.files, function(file){
32739             if(this.fireEvent('inspect', this, file) != false){
32740                 this.files.push(file);
32741             }
32742         }, this);
32743         
32744         this.queue();
32745         
32746     },
32747     
32748     queue : function()
32749     {
32750         this.selectorEl.dom.value = '';
32751         
32752         if(!this.files || !this.files.length){
32753             return;
32754         }
32755         
32756         if(this.boxes > 0 && this.files.length > this.boxes){
32757             this.files = this.files.slice(0, this.boxes);
32758         }
32759         
32760         this.uploader.show();
32761         
32762         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32763             this.uploader.hide();
32764         }
32765         
32766         var _this = this;
32767         
32768         var files = [];
32769         
32770         var docs = [];
32771         
32772         Roo.each(this.files, function(file){
32773             
32774             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32775                 var f = this.renderPreview(file);
32776                 files.push(f);
32777                 return;
32778             }
32779             
32780             if(file.type.indexOf('image') != -1){
32781                 this.delegates.push(
32782                     (function(){
32783                         _this.process(file);
32784                     }).createDelegate(this)
32785                 );
32786         
32787                 return;
32788             }
32789             
32790             docs.push(
32791                 (function(){
32792                     _this.process(file);
32793                 }).createDelegate(this)
32794             );
32795             
32796         }, this);
32797         
32798         this.files = files;
32799         
32800         this.delegates = this.delegates.concat(docs);
32801         
32802         if(!this.delegates.length){
32803             this.refresh();
32804             return;
32805         }
32806         
32807         this.progressBar.aria_valuemax = this.delegates.length;
32808         
32809         this.arrange();
32810         
32811         return;
32812     },
32813     
32814     arrange : function()
32815     {
32816         if(!this.delegates.length){
32817             this.progressDialog.hide();
32818             this.refresh();
32819             return;
32820         }
32821         
32822         var delegate = this.delegates.shift();
32823         
32824         this.progressDialog.show();
32825         
32826         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32827         
32828         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32829         
32830         delegate();
32831     },
32832     
32833     refresh : function()
32834     {
32835         this.uploader.show();
32836         
32837         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32838             this.uploader.hide();
32839         }
32840         
32841         Roo.isTouch ? this.closable(false) : this.closable(true);
32842         
32843         this.fireEvent('refresh', this);
32844     },
32845     
32846     onRemove : function(e, el, o)
32847     {
32848         e.preventDefault();
32849         
32850         this.fireEvent('remove', this, o);
32851         
32852     },
32853     
32854     remove : function(o)
32855     {
32856         var files = [];
32857         
32858         Roo.each(this.files, function(file){
32859             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32860                 files.push(file);
32861                 return;
32862             }
32863
32864             o.target.remove();
32865
32866         }, this);
32867         
32868         this.files = files;
32869         
32870         this.refresh();
32871     },
32872     
32873     clear : function()
32874     {
32875         Roo.each(this.files, function(file){
32876             if(!file.target){
32877                 return;
32878             }
32879             
32880             file.target.remove();
32881
32882         }, this);
32883         
32884         this.files = [];
32885         
32886         this.refresh();
32887     },
32888     
32889     onClick : function(e, el, o)
32890     {
32891         e.preventDefault();
32892         
32893         this.fireEvent('click', this, o);
32894         
32895     },
32896     
32897     closable : function(closable)
32898     {
32899         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32900             
32901             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32902             
32903             if(closable){
32904                 el.show();
32905                 return;
32906             }
32907             
32908             el.hide();
32909             
32910         }, this);
32911     },
32912     
32913     xhrOnLoad : function(xhr)
32914     {
32915         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32916             el.remove();
32917         }, this);
32918         
32919         if (xhr.readyState !== 4) {
32920             this.arrange();
32921             this.fireEvent('exception', this, xhr);
32922             return;
32923         }
32924
32925         var response = Roo.decode(xhr.responseText);
32926         
32927         if(!response.success){
32928             this.arrange();
32929             this.fireEvent('exception', this, xhr);
32930             return;
32931         }
32932         
32933         var file = this.renderPreview(response.data);
32934         
32935         this.files.push(file);
32936         
32937         this.arrange();
32938         
32939         this.fireEvent('afterupload', this, xhr);
32940         
32941     },
32942     
32943     xhrOnError : function(xhr)
32944     {
32945         Roo.log('xhr on error');
32946         
32947         var response = Roo.decode(xhr.responseText);
32948           
32949         Roo.log(response);
32950         
32951         this.arrange();
32952     },
32953     
32954     process : function(file)
32955     {
32956         if(this.fireEvent('process', this, file) !== false){
32957             if(this.editable && file.type.indexOf('image') != -1){
32958                 this.fireEvent('edit', this, file);
32959                 return;
32960             }
32961
32962             this.uploadStart(file, false);
32963
32964             return;
32965         }
32966         
32967     },
32968     
32969     uploadStart : function(file, crop)
32970     {
32971         this.xhr = new XMLHttpRequest();
32972         
32973         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32974             this.arrange();
32975             return;
32976         }
32977         
32978         file.xhr = this.xhr;
32979             
32980         this.managerEl.createChild({
32981             tag : 'div',
32982             cls : 'roo-document-manager-loading',
32983             cn : [
32984                 {
32985                     tag : 'div',
32986                     tooltip : file.name,
32987                     cls : 'roo-document-manager-thumb',
32988                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32989                 }
32990             ]
32991
32992         });
32993
32994         this.xhr.open(this.method, this.url, true);
32995         
32996         var headers = {
32997             "Accept": "application/json",
32998             "Cache-Control": "no-cache",
32999             "X-Requested-With": "XMLHttpRequest"
33000         };
33001         
33002         for (var headerName in headers) {
33003             var headerValue = headers[headerName];
33004             if (headerValue) {
33005                 this.xhr.setRequestHeader(headerName, headerValue);
33006             }
33007         }
33008         
33009         var _this = this;
33010         
33011         this.xhr.onload = function()
33012         {
33013             _this.xhrOnLoad(_this.xhr);
33014         }
33015         
33016         this.xhr.onerror = function()
33017         {
33018             _this.xhrOnError(_this.xhr);
33019         }
33020         
33021         var formData = new FormData();
33022
33023         formData.append('returnHTML', 'NO');
33024         
33025         if(crop){
33026             formData.append('crop', crop);
33027         }
33028         
33029         formData.append(this.paramName, file, file.name);
33030         
33031         var options = {
33032             file : file, 
33033             manually : false
33034         };
33035         
33036         if(this.fireEvent('prepare', this, formData, options) != false){
33037             
33038             if(options.manually){
33039                 return;
33040             }
33041             
33042             this.xhr.send(formData);
33043             return;
33044         };
33045         
33046         this.uploadCancel();
33047     },
33048     
33049     uploadCancel : function()
33050     {
33051         if (this.xhr) {
33052             this.xhr.abort();
33053         }
33054         
33055         this.delegates = [];
33056         
33057         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33058             el.remove();
33059         }, this);
33060         
33061         this.arrange();
33062     },
33063     
33064     renderPreview : function(file)
33065     {
33066         if(typeof(file.target) != 'undefined' && file.target){
33067             return file;
33068         }
33069         
33070         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33071         
33072         var previewEl = this.managerEl.createChild({
33073             tag : 'div',
33074             cls : 'roo-document-manager-preview',
33075             cn : [
33076                 {
33077                     tag : 'div',
33078                     tooltip : file[this.toolTipName],
33079                     cls : 'roo-document-manager-thumb',
33080                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33081                 },
33082                 {
33083                     tag : 'button',
33084                     cls : 'close',
33085                     html : '<i class="fa fa-times-circle"></i>'
33086                 }
33087             ]
33088         });
33089
33090         var close = previewEl.select('button.close', true).first();
33091
33092         close.on('click', this.onRemove, this, file);
33093
33094         file.target = previewEl;
33095
33096         var image = previewEl.select('img', true).first();
33097         
33098         var _this = this;
33099         
33100         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33101         
33102         image.on('click', this.onClick, this, file);
33103         
33104         this.fireEvent('previewrendered', this, file);
33105         
33106         return file;
33107         
33108     },
33109     
33110     onPreviewLoad : function(file, image)
33111     {
33112         if(typeof(file.target) == 'undefined' || !file.target){
33113             return;
33114         }
33115         
33116         var width = image.dom.naturalWidth || image.dom.width;
33117         var height = image.dom.naturalHeight || image.dom.height;
33118         
33119         if(!this.previewResize) {
33120             return;
33121         }
33122         
33123         if(width > height){
33124             file.target.addClass('wide');
33125             return;
33126         }
33127         
33128         file.target.addClass('tall');
33129         return;
33130         
33131     },
33132     
33133     uploadFromSource : function(file, crop)
33134     {
33135         this.xhr = new XMLHttpRequest();
33136         
33137         this.managerEl.createChild({
33138             tag : 'div',
33139             cls : 'roo-document-manager-loading',
33140             cn : [
33141                 {
33142                     tag : 'div',
33143                     tooltip : file.name,
33144                     cls : 'roo-document-manager-thumb',
33145                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33146                 }
33147             ]
33148
33149         });
33150
33151         this.xhr.open(this.method, this.url, true);
33152         
33153         var headers = {
33154             "Accept": "application/json",
33155             "Cache-Control": "no-cache",
33156             "X-Requested-With": "XMLHttpRequest"
33157         };
33158         
33159         for (var headerName in headers) {
33160             var headerValue = headers[headerName];
33161             if (headerValue) {
33162                 this.xhr.setRequestHeader(headerName, headerValue);
33163             }
33164         }
33165         
33166         var _this = this;
33167         
33168         this.xhr.onload = function()
33169         {
33170             _this.xhrOnLoad(_this.xhr);
33171         }
33172         
33173         this.xhr.onerror = function()
33174         {
33175             _this.xhrOnError(_this.xhr);
33176         }
33177         
33178         var formData = new FormData();
33179
33180         formData.append('returnHTML', 'NO');
33181         
33182         formData.append('crop', crop);
33183         
33184         if(typeof(file.filename) != 'undefined'){
33185             formData.append('filename', file.filename);
33186         }
33187         
33188         if(typeof(file.mimetype) != 'undefined'){
33189             formData.append('mimetype', file.mimetype);
33190         }
33191         
33192         Roo.log(formData);
33193         
33194         if(this.fireEvent('prepare', this, formData) != false){
33195             this.xhr.send(formData);
33196         };
33197     }
33198 });
33199
33200 /*
33201 * Licence: LGPL
33202 */
33203
33204 /**
33205  * @class Roo.bootstrap.DocumentViewer
33206  * @extends Roo.bootstrap.Component
33207  * Bootstrap DocumentViewer class
33208  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33209  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33210  * 
33211  * @constructor
33212  * Create a new DocumentViewer
33213  * @param {Object} config The config object
33214  */
33215
33216 Roo.bootstrap.DocumentViewer = function(config){
33217     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33218     
33219     this.addEvents({
33220         /**
33221          * @event initial
33222          * Fire after initEvent
33223          * @param {Roo.bootstrap.DocumentViewer} this
33224          */
33225         "initial" : true,
33226         /**
33227          * @event click
33228          * Fire after click
33229          * @param {Roo.bootstrap.DocumentViewer} this
33230          */
33231         "click" : true,
33232         /**
33233          * @event download
33234          * Fire after download button
33235          * @param {Roo.bootstrap.DocumentViewer} this
33236          */
33237         "download" : true,
33238         /**
33239          * @event trash
33240          * Fire after trash button
33241          * @param {Roo.bootstrap.DocumentViewer} this
33242          */
33243         "trash" : true
33244         
33245     });
33246 };
33247
33248 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33249     
33250     showDownload : true,
33251     
33252     showTrash : true,
33253     
33254     getAutoCreate : function()
33255     {
33256         var cfg = {
33257             tag : 'div',
33258             cls : 'roo-document-viewer',
33259             cn : [
33260                 {
33261                     tag : 'div',
33262                     cls : 'roo-document-viewer-body',
33263                     cn : [
33264                         {
33265                             tag : 'div',
33266                             cls : 'roo-document-viewer-thumb',
33267                             cn : [
33268                                 {
33269                                     tag : 'img',
33270                                     cls : 'roo-document-viewer-image'
33271                                 }
33272                             ]
33273                         }
33274                     ]
33275                 },
33276                 {
33277                     tag : 'div',
33278                     cls : 'roo-document-viewer-footer',
33279                     cn : {
33280                         tag : 'div',
33281                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33282                         cn : [
33283                             {
33284                                 tag : 'div',
33285                                 cls : 'btn-group roo-document-viewer-download',
33286                                 cn : [
33287                                     {
33288                                         tag : 'button',
33289                                         cls : 'btn btn-default',
33290                                         html : '<i class="fa fa-download"></i>'
33291                                     }
33292                                 ]
33293                             },
33294                             {
33295                                 tag : 'div',
33296                                 cls : 'btn-group roo-document-viewer-trash',
33297                                 cn : [
33298                                     {
33299                                         tag : 'button',
33300                                         cls : 'btn btn-default',
33301                                         html : '<i class="fa fa-trash"></i>'
33302                                     }
33303                                 ]
33304                             }
33305                         ]
33306                     }
33307                 }
33308             ]
33309         };
33310         
33311         return cfg;
33312     },
33313     
33314     initEvents : function()
33315     {
33316         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33317         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33318         
33319         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33320         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33321         
33322         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33323         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33324         
33325         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33326         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33327         
33328         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33329         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33330         
33331         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33332         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33333         
33334         this.bodyEl.on('click', this.onClick, this);
33335         this.downloadBtn.on('click', this.onDownload, this);
33336         this.trashBtn.on('click', this.onTrash, this);
33337         
33338         this.downloadBtn.hide();
33339         this.trashBtn.hide();
33340         
33341         if(this.showDownload){
33342             this.downloadBtn.show();
33343         }
33344         
33345         if(this.showTrash){
33346             this.trashBtn.show();
33347         }
33348         
33349         if(!this.showDownload && !this.showTrash) {
33350             this.footerEl.hide();
33351         }
33352         
33353     },
33354     
33355     initial : function()
33356     {
33357         this.fireEvent('initial', this);
33358         
33359     },
33360     
33361     onClick : function(e)
33362     {
33363         e.preventDefault();
33364         
33365         this.fireEvent('click', this);
33366     },
33367     
33368     onDownload : function(e)
33369     {
33370         e.preventDefault();
33371         
33372         this.fireEvent('download', this);
33373     },
33374     
33375     onTrash : function(e)
33376     {
33377         e.preventDefault();
33378         
33379         this.fireEvent('trash', this);
33380     }
33381     
33382 });
33383 /*
33384  * - LGPL
33385  *
33386  * FieldLabel
33387  * 
33388  */
33389
33390 /**
33391  * @class Roo.bootstrap.form.FieldLabel
33392  * @extends Roo.bootstrap.Component
33393  * Bootstrap FieldLabel class
33394  * @cfg {String} html contents of the element
33395  * @cfg {String} tag tag of the element default label
33396  * @cfg {String} cls class of the element
33397  * @cfg {String} target label target 
33398  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33399  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33400  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33401  * @cfg {String} iconTooltip default "This field is required"
33402  * @cfg {String} indicatorpos (left|right) default left
33403  * 
33404  * @constructor
33405  * Create a new FieldLabel
33406  * @param {Object} config The config object
33407  */
33408
33409 Roo.bootstrap.form.FieldLabel = function(config){
33410     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33411     
33412     this.addEvents({
33413             /**
33414              * @event invalid
33415              * Fires after the field has been marked as invalid.
33416              * @param {Roo.form.FieldLabel} this
33417              * @param {String} msg The validation message
33418              */
33419             invalid : true,
33420             /**
33421              * @event valid
33422              * Fires after the field has been validated with no errors.
33423              * @param {Roo.form.FieldLabel} this
33424              */
33425             valid : true
33426         });
33427 };
33428
33429 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33430     
33431     tag: 'label',
33432     cls: '',
33433     html: '',
33434     target: '',
33435     allowBlank : true,
33436     invalidClass : 'has-warning',
33437     validClass : 'has-success',
33438     iconTooltip : 'This field is required',
33439     indicatorpos : 'left',
33440     
33441     getAutoCreate : function(){
33442         
33443         var cls = "";
33444         if (!this.allowBlank) {
33445             cls  = "visible";
33446         }
33447         
33448         var cfg = {
33449             tag : this.tag,
33450             cls : 'roo-bootstrap-field-label ' + this.cls,
33451             for : this.target,
33452             cn : [
33453                 {
33454                     tag : 'i',
33455                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33456                     tooltip : this.iconTooltip
33457                 },
33458                 {
33459                     tag : 'span',
33460                     html : this.html
33461                 }
33462             ] 
33463         };
33464         
33465         if(this.indicatorpos == 'right'){
33466             var cfg = {
33467                 tag : this.tag,
33468                 cls : 'roo-bootstrap-field-label ' + this.cls,
33469                 for : this.target,
33470                 cn : [
33471                     {
33472                         tag : 'span',
33473                         html : this.html
33474                     },
33475                     {
33476                         tag : 'i',
33477                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33478                         tooltip : this.iconTooltip
33479                     }
33480                 ] 
33481             };
33482         }
33483         
33484         return cfg;
33485     },
33486     
33487     initEvents: function() 
33488     {
33489         Roo.bootstrap.Element.superclass.initEvents.call(this);
33490         
33491         this.indicator = this.indicatorEl();
33492         
33493         if(this.indicator){
33494             this.indicator.removeClass('visible');
33495             this.indicator.addClass('invisible');
33496         }
33497         
33498         Roo.bootstrap.form.FieldLabel.register(this);
33499     },
33500     
33501     indicatorEl : function()
33502     {
33503         var indicator = this.el.select('i.roo-required-indicator',true).first();
33504         
33505         if(!indicator){
33506             return false;
33507         }
33508         
33509         return indicator;
33510         
33511     },
33512     
33513     /**
33514      * Mark this field as valid
33515      */
33516     markValid : function()
33517     {
33518         if(this.indicator){
33519             this.indicator.removeClass('visible');
33520             this.indicator.addClass('invisible');
33521         }
33522         if (Roo.bootstrap.version == 3) {
33523             this.el.removeClass(this.invalidClass);
33524             this.el.addClass(this.validClass);
33525         } else {
33526             this.el.removeClass('is-invalid');
33527             this.el.addClass('is-valid');
33528         }
33529         
33530         
33531         this.fireEvent('valid', this);
33532     },
33533     
33534     /**
33535      * Mark this field as invalid
33536      * @param {String} msg The validation message
33537      */
33538     markInvalid : function(msg)
33539     {
33540         if(this.indicator){
33541             this.indicator.removeClass('invisible');
33542             this.indicator.addClass('visible');
33543         }
33544           if (Roo.bootstrap.version == 3) {
33545             this.el.removeClass(this.validClass);
33546             this.el.addClass(this.invalidClass);
33547         } else {
33548             this.el.removeClass('is-valid');
33549             this.el.addClass('is-invalid');
33550         }
33551         
33552         
33553         this.fireEvent('invalid', this, msg);
33554     }
33555     
33556    
33557 });
33558
33559 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33560     
33561     groups: {},
33562     
33563      /**
33564     * register a FieldLabel Group
33565     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33566     */
33567     register : function(label)
33568     {
33569         if(this.groups.hasOwnProperty(label.target)){
33570             return;
33571         }
33572      
33573         this.groups[label.target] = label;
33574         
33575     },
33576     /**
33577     * fetch a FieldLabel Group based on the target
33578     * @param {string} target
33579     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33580     */
33581     get: function(target) {
33582         if (typeof(this.groups[target]) == 'undefined') {
33583             return false;
33584         }
33585         
33586         return this.groups[target] ;
33587     }
33588 });
33589
33590  
33591
33592  /*
33593  * - LGPL
33594  *
33595  * page DateSplitField.
33596  * 
33597  */
33598
33599
33600 /**
33601  * @class Roo.bootstrap.form.DateSplitField
33602  * @extends Roo.bootstrap.Component
33603  * Bootstrap DateSplitField class
33604  * @cfg {string} fieldLabel - the label associated
33605  * @cfg {Number} labelWidth set the width of label (0-12)
33606  * @cfg {String} labelAlign (top|left)
33607  * @cfg {Boolean} dayAllowBlank (true|false) default false
33608  * @cfg {Boolean} monthAllowBlank (true|false) default false
33609  * @cfg {Boolean} yearAllowBlank (true|false) default false
33610  * @cfg {string} dayPlaceholder 
33611  * @cfg {string} monthPlaceholder
33612  * @cfg {string} yearPlaceholder
33613  * @cfg {string} dayFormat default 'd'
33614  * @cfg {string} monthFormat default 'm'
33615  * @cfg {string} yearFormat default 'Y'
33616  * @cfg {Number} labellg set the width of label (1-12)
33617  * @cfg {Number} labelmd set the width of label (1-12)
33618  * @cfg {Number} labelsm set the width of label (1-12)
33619  * @cfg {Number} labelxs set the width of label (1-12)
33620
33621  *     
33622  * @constructor
33623  * Create a new DateSplitField
33624  * @param {Object} config The config object
33625  */
33626
33627 Roo.bootstrap.form.DateSplitField = function(config){
33628     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33629     
33630     this.addEvents({
33631         // raw events
33632          /**
33633          * @event years
33634          * getting the data of years
33635          * @param {Roo.bootstrap.form.DateSplitField} this
33636          * @param {Object} years
33637          */
33638         "years" : true,
33639         /**
33640          * @event days
33641          * getting the data of days
33642          * @param {Roo.bootstrap.form.DateSplitField} this
33643          * @param {Object} days
33644          */
33645         "days" : true,
33646         /**
33647          * @event invalid
33648          * Fires after the field has been marked as invalid.
33649          * @param {Roo.form.Field} this
33650          * @param {String} msg The validation message
33651          */
33652         invalid : true,
33653        /**
33654          * @event valid
33655          * Fires after the field has been validated with no errors.
33656          * @param {Roo.form.Field} this
33657          */
33658         valid : true
33659     });
33660 };
33661
33662 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33663     
33664     fieldLabel : '',
33665     labelAlign : 'top',
33666     labelWidth : 3,
33667     dayAllowBlank : false,
33668     monthAllowBlank : false,
33669     yearAllowBlank : false,
33670     dayPlaceholder : '',
33671     monthPlaceholder : '',
33672     yearPlaceholder : '',
33673     dayFormat : 'd',
33674     monthFormat : 'm',
33675     yearFormat : 'Y',
33676     isFormField : true,
33677     labellg : 0,
33678     labelmd : 0,
33679     labelsm : 0,
33680     labelxs : 0,
33681     
33682     getAutoCreate : function()
33683     {
33684         var cfg = {
33685             tag : 'div',
33686             cls : 'row roo-date-split-field-group',
33687             cn : [
33688                 {
33689                     tag : 'input',
33690                     type : 'hidden',
33691                     cls : 'form-hidden-field roo-date-split-field-group-value',
33692                     name : this.name
33693                 }
33694             ]
33695         };
33696         
33697         var labelCls = 'col-md-12';
33698         var contentCls = 'col-md-4';
33699         
33700         if(this.fieldLabel){
33701             
33702             var label = {
33703                 tag : 'div',
33704                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33705                 cn : [
33706                     {
33707                         tag : 'label',
33708                         html : this.fieldLabel
33709                     }
33710                 ]
33711             };
33712             
33713             if(this.labelAlign == 'left'){
33714             
33715                 if(this.labelWidth > 12){
33716                     label.style = "width: " + this.labelWidth + 'px';
33717                 }
33718
33719                 if(this.labelWidth < 13 && this.labelmd == 0){
33720                     this.labelmd = this.labelWidth;
33721                 }
33722
33723                 if(this.labellg > 0){
33724                     labelCls = ' col-lg-' + this.labellg;
33725                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33726                 }
33727
33728                 if(this.labelmd > 0){
33729                     labelCls = ' col-md-' + this.labelmd;
33730                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33731                 }
33732
33733                 if(this.labelsm > 0){
33734                     labelCls = ' col-sm-' + this.labelsm;
33735                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33736                 }
33737
33738                 if(this.labelxs > 0){
33739                     labelCls = ' col-xs-' + this.labelxs;
33740                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33741                 }
33742             }
33743             
33744             label.cls += ' ' + labelCls;
33745             
33746             cfg.cn.push(label);
33747         }
33748         
33749         Roo.each(['day', 'month', 'year'], function(t){
33750             cfg.cn.push({
33751                 tag : 'div',
33752                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33753             });
33754         }, this);
33755         
33756         return cfg;
33757     },
33758     
33759     inputEl: function ()
33760     {
33761         return this.el.select('.roo-date-split-field-group-value', true).first();
33762     },
33763     
33764     onRender : function(ct, position) 
33765     {
33766         var _this = this;
33767         
33768         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33769         
33770         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33771         
33772         this.dayField = new Roo.bootstrap.form.ComboBox({
33773             allowBlank : this.dayAllowBlank,
33774             alwaysQuery : true,
33775             displayField : 'value',
33776             editable : false,
33777             fieldLabel : '',
33778             forceSelection : true,
33779             mode : 'local',
33780             placeholder : this.dayPlaceholder,
33781             selectOnFocus : true,
33782             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33783             triggerAction : 'all',
33784             typeAhead : true,
33785             valueField : 'value',
33786             store : new Roo.data.SimpleStore({
33787                 data : (function() {    
33788                     var days = [];
33789                     _this.fireEvent('days', _this, days);
33790                     return days;
33791                 })(),
33792                 fields : [ 'value' ]
33793             }),
33794             listeners : {
33795                 select : function (_self, record, index)
33796                 {
33797                     _this.setValue(_this.getValue());
33798                 }
33799             }
33800         });
33801
33802         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33803         
33804         this.monthField = new Roo.bootstrap.form.MonthField({
33805             after : '<i class=\"fa fa-calendar\"></i>',
33806             allowBlank : this.monthAllowBlank,
33807             placeholder : this.monthPlaceholder,
33808             readOnly : true,
33809             listeners : {
33810                 render : function (_self)
33811                 {
33812                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33813                         e.preventDefault();
33814                         _self.focus();
33815                     });
33816                 },
33817                 select : function (_self, oldvalue, newvalue)
33818                 {
33819                     _this.setValue(_this.getValue());
33820                 }
33821             }
33822         });
33823         
33824         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33825         
33826         this.yearField = new Roo.bootstrap.form.ComboBox({
33827             allowBlank : this.yearAllowBlank,
33828             alwaysQuery : true,
33829             displayField : 'value',
33830             editable : false,
33831             fieldLabel : '',
33832             forceSelection : true,
33833             mode : 'local',
33834             placeholder : this.yearPlaceholder,
33835             selectOnFocus : true,
33836             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33837             triggerAction : 'all',
33838             typeAhead : true,
33839             valueField : 'value',
33840             store : new Roo.data.SimpleStore({
33841                 data : (function() {
33842                     var years = [];
33843                     _this.fireEvent('years', _this, years);
33844                     return years;
33845                 })(),
33846                 fields : [ 'value' ]
33847             }),
33848             listeners : {
33849                 select : function (_self, record, index)
33850                 {
33851                     _this.setValue(_this.getValue());
33852                 }
33853             }
33854         });
33855
33856         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33857     },
33858     
33859     setValue : function(v, format)
33860     {
33861         this.inputEl.dom.value = v;
33862         
33863         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33864         
33865         var d = Date.parseDate(v, f);
33866         
33867         if(!d){
33868             this.validate();
33869             return;
33870         }
33871         
33872         this.setDay(d.format(this.dayFormat));
33873         this.setMonth(d.format(this.monthFormat));
33874         this.setYear(d.format(this.yearFormat));
33875         
33876         this.validate();
33877         
33878         return;
33879     },
33880     
33881     setDay : function(v)
33882     {
33883         this.dayField.setValue(v);
33884         this.inputEl.dom.value = this.getValue();
33885         this.validate();
33886         return;
33887     },
33888     
33889     setMonth : function(v)
33890     {
33891         this.monthField.setValue(v, true);
33892         this.inputEl.dom.value = this.getValue();
33893         this.validate();
33894         return;
33895     },
33896     
33897     setYear : function(v)
33898     {
33899         this.yearField.setValue(v);
33900         this.inputEl.dom.value = this.getValue();
33901         this.validate();
33902         return;
33903     },
33904     
33905     getDay : function()
33906     {
33907         return this.dayField.getValue();
33908     },
33909     
33910     getMonth : function()
33911     {
33912         return this.monthField.getValue();
33913     },
33914     
33915     getYear : function()
33916     {
33917         return this.yearField.getValue();
33918     },
33919     
33920     getValue : function()
33921     {
33922         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33923         
33924         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33925         
33926         return date;
33927     },
33928     
33929     reset : function()
33930     {
33931         this.setDay('');
33932         this.setMonth('');
33933         this.setYear('');
33934         this.inputEl.dom.value = '';
33935         this.validate();
33936         return;
33937     },
33938     
33939     validate : function()
33940     {
33941         var d = this.dayField.validate();
33942         var m = this.monthField.validate();
33943         var y = this.yearField.validate();
33944         
33945         var valid = true;
33946         
33947         if(
33948                 (!this.dayAllowBlank && !d) ||
33949                 (!this.monthAllowBlank && !m) ||
33950                 (!this.yearAllowBlank && !y)
33951         ){
33952             valid = false;
33953         }
33954         
33955         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33956             return valid;
33957         }
33958         
33959         if(valid){
33960             this.markValid();
33961             return valid;
33962         }
33963         
33964         this.markInvalid();
33965         
33966         return valid;
33967     },
33968     
33969     markValid : function()
33970     {
33971         
33972         var label = this.el.select('label', true).first();
33973         var icon = this.el.select('i.fa-star', true).first();
33974
33975         if(label && icon){
33976             icon.remove();
33977         }
33978         
33979         this.fireEvent('valid', this);
33980     },
33981     
33982      /**
33983      * Mark this field as invalid
33984      * @param {String} msg The validation message
33985      */
33986     markInvalid : function(msg)
33987     {
33988         
33989         var label = this.el.select('label', true).first();
33990         var icon = this.el.select('i.fa-star', true).first();
33991
33992         if(label && !icon){
33993             this.el.select('.roo-date-split-field-label', true).createChild({
33994                 tag : 'i',
33995                 cls : 'text-danger fa fa-lg fa-star',
33996                 tooltip : 'This field is required',
33997                 style : 'margin-right:5px;'
33998             }, label, true);
33999         }
34000         
34001         this.fireEvent('invalid', this, msg);
34002     },
34003     
34004     clearInvalid : function()
34005     {
34006         var label = this.el.select('label', true).first();
34007         var icon = this.el.select('i.fa-star', true).first();
34008
34009         if(label && icon){
34010             icon.remove();
34011         }
34012         
34013         this.fireEvent('valid', this);
34014     },
34015     
34016     getName: function()
34017     {
34018         return this.name;
34019     }
34020     
34021 });
34022
34023  
34024
34025 /**
34026  * @class Roo.bootstrap.LayoutMasonry
34027  * @extends Roo.bootstrap.Component
34028  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34029  * Bootstrap Layout Masonry class
34030  *
34031  * This is based on 
34032  * http://masonry.desandro.com
34033  *
34034  * The idea is to render all the bricks based on vertical width...
34035  *
34036  * The original code extends 'outlayer' - we might need to use that....
34037
34038  * @constructor
34039  * Create a new Element
34040  * @param {Object} config The config object
34041  */
34042
34043 Roo.bootstrap.LayoutMasonry = function(config){
34044     
34045     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34046     
34047     this.bricks = [];
34048     
34049     Roo.bootstrap.LayoutMasonry.register(this);
34050     
34051     this.addEvents({
34052         // raw events
34053         /**
34054          * @event layout
34055          * Fire after layout the items
34056          * @param {Roo.bootstrap.LayoutMasonry} this
34057          * @param {Roo.EventObject} e
34058          */
34059         "layout" : true
34060     });
34061     
34062 };
34063
34064 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34065     
34066     /**
34067      * @cfg {Boolean} isLayoutInstant = no animation?
34068      */   
34069     isLayoutInstant : false, // needed?
34070    
34071     /**
34072      * @cfg {Number} boxWidth  width of the columns
34073      */   
34074     boxWidth : 450,
34075     
34076       /**
34077      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34078      */   
34079     boxHeight : 0,
34080     
34081     /**
34082      * @cfg {Number} padWidth padding below box..
34083      */   
34084     padWidth : 10, 
34085     
34086     /**
34087      * @cfg {Number} gutter gutter width..
34088      */   
34089     gutter : 10,
34090     
34091      /**
34092      * @cfg {Number} maxCols maximum number of columns
34093      */   
34094     
34095     maxCols: 0,
34096     
34097     /**
34098      * @cfg {Boolean} isAutoInitial defalut true
34099      */   
34100     isAutoInitial : true, 
34101     
34102     containerWidth: 0,
34103     
34104     /**
34105      * @cfg {Boolean} isHorizontal defalut false
34106      */   
34107     isHorizontal : false, 
34108
34109     currentSize : null,
34110     
34111     tag: 'div',
34112     
34113     cls: '',
34114     
34115     bricks: null, //CompositeElement
34116     
34117     cols : 1,
34118     
34119     _isLayoutInited : false,
34120     
34121 //    isAlternative : false, // only use for vertical layout...
34122     
34123     /**
34124      * @cfg {Number} alternativePadWidth padding below box..
34125      */   
34126     alternativePadWidth : 50,
34127     
34128     selectedBrick : [],
34129     
34130     getAutoCreate : function(){
34131         
34132         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34133         
34134         var cfg = {
34135             tag: this.tag,
34136             cls: 'blog-masonary-wrapper ' + this.cls,
34137             cn : {
34138                 cls : 'mas-boxes masonary'
34139             }
34140         };
34141         
34142         return cfg;
34143     },
34144     
34145     getChildContainer: function( )
34146     {
34147         if (this.boxesEl) {
34148             return this.boxesEl;
34149         }
34150         
34151         this.boxesEl = this.el.select('.mas-boxes').first();
34152         
34153         return this.boxesEl;
34154     },
34155     
34156     
34157     initEvents : function()
34158     {
34159         var _this = this;
34160         
34161         if(this.isAutoInitial){
34162             Roo.log('hook children rendered');
34163             this.on('childrenrendered', function() {
34164                 Roo.log('children rendered');
34165                 _this.initial();
34166             } ,this);
34167         }
34168     },
34169     
34170     initial : function()
34171     {
34172         this.selectedBrick = [];
34173         
34174         this.currentSize = this.el.getBox(true);
34175         
34176         Roo.EventManager.onWindowResize(this.resize, this); 
34177
34178         if(!this.isAutoInitial){
34179             this.layout();
34180             return;
34181         }
34182         
34183         this.layout();
34184         
34185         return;
34186         //this.layout.defer(500,this);
34187         
34188     },
34189     
34190     resize : function()
34191     {
34192         var cs = this.el.getBox(true);
34193         
34194         if (
34195                 this.currentSize.width == cs.width && 
34196                 this.currentSize.x == cs.x && 
34197                 this.currentSize.height == cs.height && 
34198                 this.currentSize.y == cs.y 
34199         ) {
34200             Roo.log("no change in with or X or Y");
34201             return;
34202         }
34203         
34204         this.currentSize = cs;
34205         
34206         this.layout();
34207         
34208     },
34209     
34210     layout : function()
34211     {   
34212         this._resetLayout();
34213         
34214         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34215         
34216         this.layoutItems( isInstant );
34217       
34218         this._isLayoutInited = true;
34219         
34220         this.fireEvent('layout', this);
34221         
34222     },
34223     
34224     _resetLayout : function()
34225     {
34226         if(this.isHorizontal){
34227             this.horizontalMeasureColumns();
34228             return;
34229         }
34230         
34231         this.verticalMeasureColumns();
34232         
34233     },
34234     
34235     verticalMeasureColumns : function()
34236     {
34237         this.getContainerWidth();
34238         
34239 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34240 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34241 //            return;
34242 //        }
34243         
34244         var boxWidth = this.boxWidth + this.padWidth;
34245         
34246         if(this.containerWidth < this.boxWidth){
34247             boxWidth = this.containerWidth
34248         }
34249         
34250         var containerWidth = this.containerWidth;
34251         
34252         var cols = Math.floor(containerWidth / boxWidth);
34253         
34254         this.cols = Math.max( cols, 1 );
34255         
34256         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34257         
34258         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34259         
34260         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34261         
34262         this.colWidth = boxWidth + avail - this.padWidth;
34263         
34264         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34265         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34266     },
34267     
34268     horizontalMeasureColumns : function()
34269     {
34270         this.getContainerWidth();
34271         
34272         var boxWidth = this.boxWidth;
34273         
34274         if(this.containerWidth < boxWidth){
34275             boxWidth = this.containerWidth;
34276         }
34277         
34278         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34279         
34280         this.el.setHeight(boxWidth);
34281         
34282     },
34283     
34284     getContainerWidth : function()
34285     {
34286         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34287     },
34288     
34289     layoutItems : function( isInstant )
34290     {
34291         Roo.log(this.bricks);
34292         
34293         var items = Roo.apply([], this.bricks);
34294         
34295         if(this.isHorizontal){
34296             this._horizontalLayoutItems( items , isInstant );
34297             return;
34298         }
34299         
34300 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34301 //            this._verticalAlternativeLayoutItems( items , isInstant );
34302 //            return;
34303 //        }
34304         
34305         this._verticalLayoutItems( items , isInstant );
34306         
34307     },
34308     
34309     _verticalLayoutItems : function ( items , isInstant)
34310     {
34311         if ( !items || !items.length ) {
34312             return;
34313         }
34314         
34315         var standard = [
34316             ['xs', 'xs', 'xs', 'tall'],
34317             ['xs', 'xs', 'tall'],
34318             ['xs', 'xs', 'sm'],
34319             ['xs', 'xs', 'xs'],
34320             ['xs', 'tall'],
34321             ['xs', 'sm'],
34322             ['xs', 'xs'],
34323             ['xs'],
34324             
34325             ['sm', 'xs', 'xs'],
34326             ['sm', 'xs'],
34327             ['sm'],
34328             
34329             ['tall', 'xs', 'xs', 'xs'],
34330             ['tall', 'xs', 'xs'],
34331             ['tall', 'xs'],
34332             ['tall']
34333             
34334         ];
34335         
34336         var queue = [];
34337         
34338         var boxes = [];
34339         
34340         var box = [];
34341         
34342         Roo.each(items, function(item, k){
34343             
34344             switch (item.size) {
34345                 // these layouts take up a full box,
34346                 case 'md' :
34347                 case 'md-left' :
34348                 case 'md-right' :
34349                 case 'wide' :
34350                     
34351                     if(box.length){
34352                         boxes.push(box);
34353                         box = [];
34354                     }
34355                     
34356                     boxes.push([item]);
34357                     
34358                     break;
34359                     
34360                 case 'xs' :
34361                 case 'sm' :
34362                 case 'tall' :
34363                     
34364                     box.push(item);
34365                     
34366                     break;
34367                 default :
34368                     break;
34369                     
34370             }
34371             
34372         }, this);
34373         
34374         if(box.length){
34375             boxes.push(box);
34376             box = [];
34377         }
34378         
34379         var filterPattern = function(box, length)
34380         {
34381             if(!box.length){
34382                 return;
34383             }
34384             
34385             var match = false;
34386             
34387             var pattern = box.slice(0, length);
34388             
34389             var format = [];
34390             
34391             Roo.each(pattern, function(i){
34392                 format.push(i.size);
34393             }, this);
34394             
34395             Roo.each(standard, function(s){
34396                 
34397                 if(String(s) != String(format)){
34398                     return;
34399                 }
34400                 
34401                 match = true;
34402                 return false;
34403                 
34404             }, this);
34405             
34406             if(!match && length == 1){
34407                 return;
34408             }
34409             
34410             if(!match){
34411                 filterPattern(box, length - 1);
34412                 return;
34413             }
34414                 
34415             queue.push(pattern);
34416
34417             box = box.slice(length, box.length);
34418
34419             filterPattern(box, 4);
34420
34421             return;
34422             
34423         }
34424         
34425         Roo.each(boxes, function(box, k){
34426             
34427             if(!box.length){
34428                 return;
34429             }
34430             
34431             if(box.length == 1){
34432                 queue.push(box);
34433                 return;
34434             }
34435             
34436             filterPattern(box, 4);
34437             
34438         }, this);
34439         
34440         this._processVerticalLayoutQueue( queue, isInstant );
34441         
34442     },
34443     
34444 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34445 //    {
34446 //        if ( !items || !items.length ) {
34447 //            return;
34448 //        }
34449 //
34450 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34451 //        
34452 //    },
34453     
34454     _horizontalLayoutItems : function ( items , isInstant)
34455     {
34456         if ( !items || !items.length || items.length < 3) {
34457             return;
34458         }
34459         
34460         items.reverse();
34461         
34462         var eItems = items.slice(0, 3);
34463         
34464         items = items.slice(3, items.length);
34465         
34466         var standard = [
34467             ['xs', 'xs', 'xs', 'wide'],
34468             ['xs', 'xs', 'wide'],
34469             ['xs', 'xs', 'sm'],
34470             ['xs', 'xs', 'xs'],
34471             ['xs', 'wide'],
34472             ['xs', 'sm'],
34473             ['xs', 'xs'],
34474             ['xs'],
34475             
34476             ['sm', 'xs', 'xs'],
34477             ['sm', 'xs'],
34478             ['sm'],
34479             
34480             ['wide', 'xs', 'xs', 'xs'],
34481             ['wide', 'xs', 'xs'],
34482             ['wide', 'xs'],
34483             ['wide'],
34484             
34485             ['wide-thin']
34486         ];
34487         
34488         var queue = [];
34489         
34490         var boxes = [];
34491         
34492         var box = [];
34493         
34494         Roo.each(items, function(item, k){
34495             
34496             switch (item.size) {
34497                 case 'md' :
34498                 case 'md-left' :
34499                 case 'md-right' :
34500                 case 'tall' :
34501                     
34502                     if(box.length){
34503                         boxes.push(box);
34504                         box = [];
34505                     }
34506                     
34507                     boxes.push([item]);
34508                     
34509                     break;
34510                     
34511                 case 'xs' :
34512                 case 'sm' :
34513                 case 'wide' :
34514                 case 'wide-thin' :
34515                     
34516                     box.push(item);
34517                     
34518                     break;
34519                 default :
34520                     break;
34521                     
34522             }
34523             
34524         }, this);
34525         
34526         if(box.length){
34527             boxes.push(box);
34528             box = [];
34529         }
34530         
34531         var filterPattern = function(box, length)
34532         {
34533             if(!box.length){
34534                 return;
34535             }
34536             
34537             var match = false;
34538             
34539             var pattern = box.slice(0, length);
34540             
34541             var format = [];
34542             
34543             Roo.each(pattern, function(i){
34544                 format.push(i.size);
34545             }, this);
34546             
34547             Roo.each(standard, function(s){
34548                 
34549                 if(String(s) != String(format)){
34550                     return;
34551                 }
34552                 
34553                 match = true;
34554                 return false;
34555                 
34556             }, this);
34557             
34558             if(!match && length == 1){
34559                 return;
34560             }
34561             
34562             if(!match){
34563                 filterPattern(box, length - 1);
34564                 return;
34565             }
34566                 
34567             queue.push(pattern);
34568
34569             box = box.slice(length, box.length);
34570
34571             filterPattern(box, 4);
34572
34573             return;
34574             
34575         }
34576         
34577         Roo.each(boxes, function(box, k){
34578             
34579             if(!box.length){
34580                 return;
34581             }
34582             
34583             if(box.length == 1){
34584                 queue.push(box);
34585                 return;
34586             }
34587             
34588             filterPattern(box, 4);
34589             
34590         }, this);
34591         
34592         
34593         var prune = [];
34594         
34595         var pos = this.el.getBox(true);
34596         
34597         var minX = pos.x;
34598         
34599         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34600         
34601         var hit_end = false;
34602         
34603         Roo.each(queue, function(box){
34604             
34605             if(hit_end){
34606                 
34607                 Roo.each(box, function(b){
34608                 
34609                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34610                     b.el.hide();
34611
34612                 }, this);
34613
34614                 return;
34615             }
34616             
34617             var mx = 0;
34618             
34619             Roo.each(box, function(b){
34620                 
34621                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34622                 b.el.show();
34623
34624                 mx = Math.max(mx, b.x);
34625                 
34626             }, this);
34627             
34628             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34629             
34630             if(maxX < minX){
34631                 
34632                 Roo.each(box, function(b){
34633                 
34634                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34635                     b.el.hide();
34636                     
34637                 }, this);
34638                 
34639                 hit_end = true;
34640                 
34641                 return;
34642             }
34643             
34644             prune.push(box);
34645             
34646         }, this);
34647         
34648         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34649     },
34650     
34651     /** Sets position of item in DOM
34652     * @param {Element} item
34653     * @param {Number} x - horizontal position
34654     * @param {Number} y - vertical position
34655     * @param {Boolean} isInstant - disables transitions
34656     */
34657     _processVerticalLayoutQueue : function( queue, isInstant )
34658     {
34659         var pos = this.el.getBox(true);
34660         var x = pos.x;
34661         var y = pos.y;
34662         var maxY = [];
34663         
34664         for (var i = 0; i < this.cols; i++){
34665             maxY[i] = pos.y;
34666         }
34667         
34668         Roo.each(queue, function(box, k){
34669             
34670             var col = k % this.cols;
34671             
34672             Roo.each(box, function(b,kk){
34673                 
34674                 b.el.position('absolute');
34675                 
34676                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34677                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34678                 
34679                 if(b.size == 'md-left' || b.size == 'md-right'){
34680                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34681                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34682                 }
34683                 
34684                 b.el.setWidth(width);
34685                 b.el.setHeight(height);
34686                 // iframe?
34687                 b.el.select('iframe',true).setSize(width,height);
34688                 
34689             }, this);
34690             
34691             for (var i = 0; i < this.cols; i++){
34692                 
34693                 if(maxY[i] < maxY[col]){
34694                     col = i;
34695                     continue;
34696                 }
34697                 
34698                 col = Math.min(col, i);
34699                 
34700             }
34701             
34702             x = pos.x + col * (this.colWidth + this.padWidth);
34703             
34704             y = maxY[col];
34705             
34706             var positions = [];
34707             
34708             switch (box.length){
34709                 case 1 :
34710                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34711                     break;
34712                 case 2 :
34713                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34714                     break;
34715                 case 3 :
34716                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34717                     break;
34718                 case 4 :
34719                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34720                     break;
34721                 default :
34722                     break;
34723             }
34724             
34725             Roo.each(box, function(b,kk){
34726                 
34727                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34728                 
34729                 var sz = b.el.getSize();
34730                 
34731                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34732                 
34733             }, this);
34734             
34735         }, this);
34736         
34737         var mY = 0;
34738         
34739         for (var i = 0; i < this.cols; i++){
34740             mY = Math.max(mY, maxY[i]);
34741         }
34742         
34743         this.el.setHeight(mY - pos.y);
34744         
34745     },
34746     
34747 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34748 //    {
34749 //        var pos = this.el.getBox(true);
34750 //        var x = pos.x;
34751 //        var y = pos.y;
34752 //        var maxX = pos.right;
34753 //        
34754 //        var maxHeight = 0;
34755 //        
34756 //        Roo.each(items, function(item, k){
34757 //            
34758 //            var c = k % 2;
34759 //            
34760 //            item.el.position('absolute');
34761 //                
34762 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34763 //
34764 //            item.el.setWidth(width);
34765 //
34766 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34767 //
34768 //            item.el.setHeight(height);
34769 //            
34770 //            if(c == 0){
34771 //                item.el.setXY([x, y], isInstant ? false : true);
34772 //            } else {
34773 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34774 //            }
34775 //            
34776 //            y = y + height + this.alternativePadWidth;
34777 //            
34778 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34779 //            
34780 //        }, this);
34781 //        
34782 //        this.el.setHeight(maxHeight);
34783 //        
34784 //    },
34785     
34786     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34787     {
34788         var pos = this.el.getBox(true);
34789         
34790         var minX = pos.x;
34791         var minY = pos.y;
34792         
34793         var maxX = pos.right;
34794         
34795         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34796         
34797         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34798         
34799         Roo.each(queue, function(box, k){
34800             
34801             Roo.each(box, function(b, kk){
34802                 
34803                 b.el.position('absolute');
34804                 
34805                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34806                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34807                 
34808                 if(b.size == 'md-left' || b.size == 'md-right'){
34809                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34810                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34811                 }
34812                 
34813                 b.el.setWidth(width);
34814                 b.el.setHeight(height);
34815                 
34816             }, this);
34817             
34818             if(!box.length){
34819                 return;
34820             }
34821             
34822             var positions = [];
34823             
34824             switch (box.length){
34825                 case 1 :
34826                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34827                     break;
34828                 case 2 :
34829                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34830                     break;
34831                 case 3 :
34832                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34833                     break;
34834                 case 4 :
34835                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34836                     break;
34837                 default :
34838                     break;
34839             }
34840             
34841             Roo.each(box, function(b,kk){
34842                 
34843                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34844                 
34845                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34846                 
34847             }, this);
34848             
34849         }, this);
34850         
34851     },
34852     
34853     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34854     {
34855         Roo.each(eItems, function(b,k){
34856             
34857             b.size = (k == 0) ? 'sm' : 'xs';
34858             b.x = (k == 0) ? 2 : 1;
34859             b.y = (k == 0) ? 2 : 1;
34860             
34861             b.el.position('absolute');
34862             
34863             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34864                 
34865             b.el.setWidth(width);
34866             
34867             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34868             
34869             b.el.setHeight(height);
34870             
34871         }, this);
34872
34873         var positions = [];
34874         
34875         positions.push({
34876             x : maxX - this.unitWidth * 2 - this.gutter,
34877             y : minY
34878         });
34879         
34880         positions.push({
34881             x : maxX - this.unitWidth,
34882             y : minY + (this.unitWidth + this.gutter) * 2
34883         });
34884         
34885         positions.push({
34886             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34887             y : minY
34888         });
34889         
34890         Roo.each(eItems, function(b,k){
34891             
34892             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34893
34894         }, this);
34895         
34896     },
34897     
34898     getVerticalOneBoxColPositions : function(x, y, box)
34899     {
34900         var pos = [];
34901         
34902         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34903         
34904         if(box[0].size == 'md-left'){
34905             rand = 0;
34906         }
34907         
34908         if(box[0].size == 'md-right'){
34909             rand = 1;
34910         }
34911         
34912         pos.push({
34913             x : x + (this.unitWidth + this.gutter) * rand,
34914             y : y
34915         });
34916         
34917         return pos;
34918     },
34919     
34920     getVerticalTwoBoxColPositions : function(x, y, box)
34921     {
34922         var pos = [];
34923         
34924         if(box[0].size == 'xs'){
34925             
34926             pos.push({
34927                 x : x,
34928                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34929             });
34930
34931             pos.push({
34932                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34933                 y : y
34934             });
34935             
34936             return pos;
34937             
34938         }
34939         
34940         pos.push({
34941             x : x,
34942             y : y
34943         });
34944
34945         pos.push({
34946             x : x + (this.unitWidth + this.gutter) * 2,
34947             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34948         });
34949         
34950         return pos;
34951         
34952     },
34953     
34954     getVerticalThreeBoxColPositions : function(x, y, box)
34955     {
34956         var pos = [];
34957         
34958         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34959             
34960             pos.push({
34961                 x : x,
34962                 y : y
34963             });
34964
34965             pos.push({
34966                 x : x + (this.unitWidth + this.gutter) * 1,
34967                 y : y
34968             });
34969             
34970             pos.push({
34971                 x : x + (this.unitWidth + this.gutter) * 2,
34972                 y : y
34973             });
34974             
34975             return pos;
34976             
34977         }
34978         
34979         if(box[0].size == 'xs' && box[1].size == 'xs'){
34980             
34981             pos.push({
34982                 x : x,
34983                 y : y
34984             });
34985
34986             pos.push({
34987                 x : x,
34988                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34989             });
34990             
34991             pos.push({
34992                 x : x + (this.unitWidth + this.gutter) * 1,
34993                 y : y
34994             });
34995             
34996             return pos;
34997             
34998         }
34999         
35000         pos.push({
35001             x : x,
35002             y : y
35003         });
35004
35005         pos.push({
35006             x : x + (this.unitWidth + this.gutter) * 2,
35007             y : y
35008         });
35009
35010         pos.push({
35011             x : x + (this.unitWidth + this.gutter) * 2,
35012             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35013         });
35014             
35015         return pos;
35016         
35017     },
35018     
35019     getVerticalFourBoxColPositions : function(x, y, box)
35020     {
35021         var pos = [];
35022         
35023         if(box[0].size == 'xs'){
35024             
35025             pos.push({
35026                 x : x,
35027                 y : y
35028             });
35029
35030             pos.push({
35031                 x : x,
35032                 y : y + (this.unitHeight + this.gutter) * 1
35033             });
35034             
35035             pos.push({
35036                 x : x,
35037                 y : y + (this.unitHeight + this.gutter) * 2
35038             });
35039             
35040             pos.push({
35041                 x : x + (this.unitWidth + this.gutter) * 1,
35042                 y : y
35043             });
35044             
35045             return pos;
35046             
35047         }
35048         
35049         pos.push({
35050             x : x,
35051             y : y
35052         });
35053
35054         pos.push({
35055             x : x + (this.unitWidth + this.gutter) * 2,
35056             y : y
35057         });
35058
35059         pos.push({
35060             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35061             y : y + (this.unitHeight + this.gutter) * 1
35062         });
35063
35064         pos.push({
35065             x : x + (this.unitWidth + this.gutter) * 2,
35066             y : y + (this.unitWidth + this.gutter) * 2
35067         });
35068
35069         return pos;
35070         
35071     },
35072     
35073     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35074     {
35075         var pos = [];
35076         
35077         if(box[0].size == 'md-left'){
35078             pos.push({
35079                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35080                 y : minY
35081             });
35082             
35083             return pos;
35084         }
35085         
35086         if(box[0].size == 'md-right'){
35087             pos.push({
35088                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35089                 y : minY + (this.unitWidth + this.gutter) * 1
35090             });
35091             
35092             return pos;
35093         }
35094         
35095         var rand = Math.floor(Math.random() * (4 - box[0].y));
35096         
35097         pos.push({
35098             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35099             y : minY + (this.unitWidth + this.gutter) * rand
35100         });
35101         
35102         return pos;
35103         
35104     },
35105     
35106     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35107     {
35108         var pos = [];
35109         
35110         if(box[0].size == 'xs'){
35111             
35112             pos.push({
35113                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35114                 y : minY
35115             });
35116
35117             pos.push({
35118                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35119                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35120             });
35121             
35122             return pos;
35123             
35124         }
35125         
35126         pos.push({
35127             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35128             y : minY
35129         });
35130
35131         pos.push({
35132             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35133             y : minY + (this.unitWidth + this.gutter) * 2
35134         });
35135         
35136         return pos;
35137         
35138     },
35139     
35140     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35141     {
35142         var pos = [];
35143         
35144         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35145             
35146             pos.push({
35147                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35148                 y : minY
35149             });
35150
35151             pos.push({
35152                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35153                 y : minY + (this.unitWidth + this.gutter) * 1
35154             });
35155             
35156             pos.push({
35157                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35158                 y : minY + (this.unitWidth + this.gutter) * 2
35159             });
35160             
35161             return pos;
35162             
35163         }
35164         
35165         if(box[0].size == 'xs' && box[1].size == 'xs'){
35166             
35167             pos.push({
35168                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35169                 y : minY
35170             });
35171
35172             pos.push({
35173                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35174                 y : minY
35175             });
35176             
35177             pos.push({
35178                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35179                 y : minY + (this.unitWidth + this.gutter) * 1
35180             });
35181             
35182             return pos;
35183             
35184         }
35185         
35186         pos.push({
35187             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35188             y : minY
35189         });
35190
35191         pos.push({
35192             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35193             y : minY + (this.unitWidth + this.gutter) * 2
35194         });
35195
35196         pos.push({
35197             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35198             y : minY + (this.unitWidth + this.gutter) * 2
35199         });
35200             
35201         return pos;
35202         
35203     },
35204     
35205     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35206     {
35207         var pos = [];
35208         
35209         if(box[0].size == 'xs'){
35210             
35211             pos.push({
35212                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
35218                 y : minY
35219             });
35220             
35221             pos.push({
35222                 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),
35223                 y : minY
35224             });
35225             
35226             pos.push({
35227                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35228                 y : minY + (this.unitWidth + this.gutter) * 1
35229             });
35230             
35231             return pos;
35232             
35233         }
35234         
35235         pos.push({
35236             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35237             y : minY
35238         });
35239         
35240         pos.push({
35241             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
35247             y : minY + (this.unitWidth + this.gutter) * 2
35248         });
35249         
35250         pos.push({
35251             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),
35252             y : minY + (this.unitWidth + this.gutter) * 2
35253         });
35254
35255         return pos;
35256         
35257     },
35258     
35259     /**
35260     * remove a Masonry Brick
35261     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35262     */
35263     removeBrick : function(brick_id)
35264     {
35265         if (!brick_id) {
35266             return;
35267         }
35268         
35269         for (var i = 0; i<this.bricks.length; i++) {
35270             if (this.bricks[i].id == brick_id) {
35271                 this.bricks.splice(i,1);
35272                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35273                 this.initial();
35274             }
35275         }
35276     },
35277     
35278     /**
35279     * adds a Masonry Brick
35280     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35281     */
35282     addBrick : function(cfg)
35283     {
35284         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35285         //this.register(cn);
35286         cn.parentId = this.id;
35287         cn.render(this.el);
35288         return cn;
35289     },
35290     
35291     /**
35292     * register a Masonry Brick
35293     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35294     */
35295     
35296     register : function(brick)
35297     {
35298         this.bricks.push(brick);
35299         brick.masonryId = this.id;
35300     },
35301     
35302     /**
35303     * clear all the Masonry Brick
35304     */
35305     clearAll : function()
35306     {
35307         this.bricks = [];
35308         //this.getChildContainer().dom.innerHTML = "";
35309         this.el.dom.innerHTML = '';
35310     },
35311     
35312     getSelected : function()
35313     {
35314         if (!this.selectedBrick) {
35315             return false;
35316         }
35317         
35318         return this.selectedBrick;
35319     }
35320 });
35321
35322 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35323     
35324     groups: {},
35325      /**
35326     * register a Masonry Layout
35327     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35328     */
35329     
35330     register : function(layout)
35331     {
35332         this.groups[layout.id] = layout;
35333     },
35334     /**
35335     * fetch a  Masonry Layout based on the masonry layout ID
35336     * @param {string} the masonry layout to add
35337     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35338     */
35339     
35340     get: function(layout_id) {
35341         if (typeof(this.groups[layout_id]) == 'undefined') {
35342             return false;
35343         }
35344         return this.groups[layout_id] ;
35345     }
35346     
35347     
35348     
35349 });
35350
35351  
35352
35353  /**
35354  *
35355  * This is based on 
35356  * http://masonry.desandro.com
35357  *
35358  * The idea is to render all the bricks based on vertical width...
35359  *
35360  * The original code extends 'outlayer' - we might need to use that....
35361  * 
35362  */
35363
35364
35365 /**
35366  * @class Roo.bootstrap.LayoutMasonryAuto
35367  * @extends Roo.bootstrap.Component
35368  * Bootstrap Layout Masonry class
35369  * 
35370  * @constructor
35371  * Create a new Element
35372  * @param {Object} config The config object
35373  */
35374
35375 Roo.bootstrap.LayoutMasonryAuto = function(config){
35376     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35377 };
35378
35379 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35380     
35381       /**
35382      * @cfg {Boolean} isFitWidth  - resize the width..
35383      */   
35384     isFitWidth : false,  // options..
35385     /**
35386      * @cfg {Boolean} isOriginLeft = left align?
35387      */   
35388     isOriginLeft : true,
35389     /**
35390      * @cfg {Boolean} isOriginTop = top align?
35391      */   
35392     isOriginTop : false,
35393     /**
35394      * @cfg {Boolean} isLayoutInstant = no animation?
35395      */   
35396     isLayoutInstant : false, // needed?
35397     /**
35398      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35399      */   
35400     isResizingContainer : true,
35401     /**
35402      * @cfg {Number} columnWidth  width of the columns 
35403      */   
35404     
35405     columnWidth : 0,
35406     
35407     /**
35408      * @cfg {Number} maxCols maximum number of columns
35409      */   
35410     
35411     maxCols: 0,
35412     /**
35413      * @cfg {Number} padHeight padding below box..
35414      */   
35415     
35416     padHeight : 10, 
35417     
35418     /**
35419      * @cfg {Boolean} isAutoInitial defalut true
35420      */   
35421     
35422     isAutoInitial : true, 
35423     
35424     // private?
35425     gutter : 0,
35426     
35427     containerWidth: 0,
35428     initialColumnWidth : 0,
35429     currentSize : null,
35430     
35431     colYs : null, // array.
35432     maxY : 0,
35433     padWidth: 10,
35434     
35435     
35436     tag: 'div',
35437     cls: '',
35438     bricks: null, //CompositeElement
35439     cols : 0, // array?
35440     // element : null, // wrapped now this.el
35441     _isLayoutInited : null, 
35442     
35443     
35444     getAutoCreate : function(){
35445         
35446         var cfg = {
35447             tag: this.tag,
35448             cls: 'blog-masonary-wrapper ' + this.cls,
35449             cn : {
35450                 cls : 'mas-boxes masonary'
35451             }
35452         };
35453         
35454         return cfg;
35455     },
35456     
35457     getChildContainer: function( )
35458     {
35459         if (this.boxesEl) {
35460             return this.boxesEl;
35461         }
35462         
35463         this.boxesEl = this.el.select('.mas-boxes').first();
35464         
35465         return this.boxesEl;
35466     },
35467     
35468     
35469     initEvents : function()
35470     {
35471         var _this = this;
35472         
35473         if(this.isAutoInitial){
35474             Roo.log('hook children rendered');
35475             this.on('childrenrendered', function() {
35476                 Roo.log('children rendered');
35477                 _this.initial();
35478             } ,this);
35479         }
35480         
35481     },
35482     
35483     initial : function()
35484     {
35485         this.reloadItems();
35486
35487         this.currentSize = this.el.getBox(true);
35488
35489         /// was window resize... - let's see if this works..
35490         Roo.EventManager.onWindowResize(this.resize, this); 
35491
35492         if(!this.isAutoInitial){
35493             this.layout();
35494             return;
35495         }
35496         
35497         this.layout.defer(500,this);
35498     },
35499     
35500     reloadItems: function()
35501     {
35502         this.bricks = this.el.select('.masonry-brick', true);
35503         
35504         this.bricks.each(function(b) {
35505             //Roo.log(b.getSize());
35506             if (!b.attr('originalwidth')) {
35507                 b.attr('originalwidth',  b.getSize().width);
35508             }
35509             
35510         });
35511         
35512         Roo.log(this.bricks.elements.length);
35513     },
35514     
35515     resize : function()
35516     {
35517         Roo.log('resize');
35518         var cs = this.el.getBox(true);
35519         
35520         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35521             Roo.log("no change in with or X");
35522             return;
35523         }
35524         this.currentSize = cs;
35525         this.layout();
35526     },
35527     
35528     layout : function()
35529     {
35530          Roo.log('layout');
35531         this._resetLayout();
35532         //this._manageStamps();
35533       
35534         // don't animate first layout
35535         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35536         this.layoutItems( isInstant );
35537       
35538         // flag for initalized
35539         this._isLayoutInited = true;
35540     },
35541     
35542     layoutItems : function( isInstant )
35543     {
35544         //var items = this._getItemsForLayout( this.items );
35545         // original code supports filtering layout items.. we just ignore it..
35546         
35547         this._layoutItems( this.bricks , isInstant );
35548       
35549         this._postLayout();
35550     },
35551     _layoutItems : function ( items , isInstant)
35552     {
35553        //this.fireEvent( 'layout', this, items );
35554     
35555
35556         if ( !items || !items.elements.length ) {
35557           // no items, emit event with empty array
35558             return;
35559         }
35560
35561         var queue = [];
35562         items.each(function(item) {
35563             Roo.log("layout item");
35564             Roo.log(item);
35565             // get x/y object from method
35566             var position = this._getItemLayoutPosition( item );
35567             // enqueue
35568             position.item = item;
35569             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35570             queue.push( position );
35571         }, this);
35572       
35573         this._processLayoutQueue( queue );
35574     },
35575     /** Sets position of item in DOM
35576     * @param {Element} item
35577     * @param {Number} x - horizontal position
35578     * @param {Number} y - vertical position
35579     * @param {Boolean} isInstant - disables transitions
35580     */
35581     _processLayoutQueue : function( queue )
35582     {
35583         for ( var i=0, len = queue.length; i < len; i++ ) {
35584             var obj = queue[i];
35585             obj.item.position('absolute');
35586             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35587         }
35588     },
35589       
35590     
35591     /**
35592     * Any logic you want to do after each layout,
35593     * i.e. size the container
35594     */
35595     _postLayout : function()
35596     {
35597         this.resizeContainer();
35598     },
35599     
35600     resizeContainer : function()
35601     {
35602         if ( !this.isResizingContainer ) {
35603             return;
35604         }
35605         var size = this._getContainerSize();
35606         if ( size ) {
35607             this.el.setSize(size.width,size.height);
35608             this.boxesEl.setSize(size.width,size.height);
35609         }
35610     },
35611     
35612     
35613     
35614     _resetLayout : function()
35615     {
35616         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35617         this.colWidth = this.el.getWidth();
35618         //this.gutter = this.el.getWidth(); 
35619         
35620         this.measureColumns();
35621
35622         // reset column Y
35623         var i = this.cols;
35624         this.colYs = [];
35625         while (i--) {
35626             this.colYs.push( 0 );
35627         }
35628     
35629         this.maxY = 0;
35630     },
35631
35632     measureColumns : function()
35633     {
35634         this.getContainerWidth();
35635       // if columnWidth is 0, default to outerWidth of first item
35636         if ( !this.columnWidth ) {
35637             var firstItem = this.bricks.first();
35638             Roo.log(firstItem);
35639             this.columnWidth  = this.containerWidth;
35640             if (firstItem && firstItem.attr('originalwidth') ) {
35641                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35642             }
35643             // columnWidth fall back to item of first element
35644             Roo.log("set column width?");
35645                         this.initialColumnWidth = this.columnWidth  ;
35646
35647             // if first elem has no width, default to size of container
35648             
35649         }
35650         
35651         
35652         if (this.initialColumnWidth) {
35653             this.columnWidth = this.initialColumnWidth;
35654         }
35655         
35656         
35657             
35658         // column width is fixed at the top - however if container width get's smaller we should
35659         // reduce it...
35660         
35661         // this bit calcs how man columns..
35662             
35663         var columnWidth = this.columnWidth += this.gutter;
35664       
35665         // calculate columns
35666         var containerWidth = this.containerWidth + this.gutter;
35667         
35668         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35669         // fix rounding errors, typically with gutters
35670         var excess = columnWidth - containerWidth % columnWidth;
35671         
35672         
35673         // if overshoot is less than a pixel, round up, otherwise floor it
35674         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35675         cols = Math[ mathMethod ]( cols );
35676         this.cols = Math.max( cols, 1 );
35677         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35678         
35679          // padding positioning..
35680         var totalColWidth = this.cols * this.columnWidth;
35681         var padavail = this.containerWidth - totalColWidth;
35682         // so for 2 columns - we need 3 'pads'
35683         
35684         var padNeeded = (1+this.cols) * this.padWidth;
35685         
35686         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35687         
35688         this.columnWidth += padExtra
35689         //this.padWidth = Math.floor(padavail /  ( this.cols));
35690         
35691         // adjust colum width so that padding is fixed??
35692         
35693         // we have 3 columns ... total = width * 3
35694         // we have X left over... that should be used by 
35695         
35696         //if (this.expandC) {
35697             
35698         //}
35699         
35700         
35701         
35702     },
35703     
35704     getContainerWidth : function()
35705     {
35706        /* // container is parent if fit width
35707         var container = this.isFitWidth ? this.element.parentNode : this.element;
35708         // check that this.size and size are there
35709         // IE8 triggers resize on body size change, so they might not be
35710         
35711         var size = getSize( container );  //FIXME
35712         this.containerWidth = size && size.innerWidth; //FIXME
35713         */
35714          
35715         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35716         
35717     },
35718     
35719     _getItemLayoutPosition : function( item )  // what is item?
35720     {
35721         // we resize the item to our columnWidth..
35722       
35723         item.setWidth(this.columnWidth);
35724         item.autoBoxAdjust  = false;
35725         
35726         var sz = item.getSize();
35727  
35728         // how many columns does this brick span
35729         var remainder = this.containerWidth % this.columnWidth;
35730         
35731         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35732         // round if off by 1 pixel, otherwise use ceil
35733         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35734         colSpan = Math.min( colSpan, this.cols );
35735         
35736         // normally this should be '1' as we dont' currently allow multi width columns..
35737         
35738         var colGroup = this._getColGroup( colSpan );
35739         // get the minimum Y value from the columns
35740         var minimumY = Math.min.apply( Math, colGroup );
35741         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35742         
35743         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35744          
35745         // position the brick
35746         var position = {
35747             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35748             y: this.currentSize.y + minimumY + this.padHeight
35749         };
35750         
35751         Roo.log(position);
35752         // apply setHeight to necessary columns
35753         var setHeight = minimumY + sz.height + this.padHeight;
35754         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35755         
35756         var setSpan = this.cols + 1 - colGroup.length;
35757         for ( var i = 0; i < setSpan; i++ ) {
35758           this.colYs[ shortColIndex + i ] = setHeight ;
35759         }
35760       
35761         return position;
35762     },
35763     
35764     /**
35765      * @param {Number} colSpan - number of columns the element spans
35766      * @returns {Array} colGroup
35767      */
35768     _getColGroup : function( colSpan )
35769     {
35770         if ( colSpan < 2 ) {
35771           // if brick spans only one column, use all the column Ys
35772           return this.colYs;
35773         }
35774       
35775         var colGroup = [];
35776         // how many different places could this brick fit horizontally
35777         var groupCount = this.cols + 1 - colSpan;
35778         // for each group potential horizontal position
35779         for ( var i = 0; i < groupCount; i++ ) {
35780           // make an array of colY values for that one group
35781           var groupColYs = this.colYs.slice( i, i + colSpan );
35782           // and get the max value of the array
35783           colGroup[i] = Math.max.apply( Math, groupColYs );
35784         }
35785         return colGroup;
35786     },
35787     /*
35788     _manageStamp : function( stamp )
35789     {
35790         var stampSize =  stamp.getSize();
35791         var offset = stamp.getBox();
35792         // get the columns that this stamp affects
35793         var firstX = this.isOriginLeft ? offset.x : offset.right;
35794         var lastX = firstX + stampSize.width;
35795         var firstCol = Math.floor( firstX / this.columnWidth );
35796         firstCol = Math.max( 0, firstCol );
35797         
35798         var lastCol = Math.floor( lastX / this.columnWidth );
35799         // lastCol should not go over if multiple of columnWidth #425
35800         lastCol -= lastX % this.columnWidth ? 0 : 1;
35801         lastCol = Math.min( this.cols - 1, lastCol );
35802         
35803         // set colYs to bottom of the stamp
35804         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35805             stampSize.height;
35806             
35807         for ( var i = firstCol; i <= lastCol; i++ ) {
35808           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35809         }
35810     },
35811     */
35812     
35813     _getContainerSize : function()
35814     {
35815         this.maxY = Math.max.apply( Math, this.colYs );
35816         var size = {
35817             height: this.maxY
35818         };
35819       
35820         if ( this.isFitWidth ) {
35821             size.width = this._getContainerFitWidth();
35822         }
35823       
35824         return size;
35825     },
35826     
35827     _getContainerFitWidth : function()
35828     {
35829         var unusedCols = 0;
35830         // count unused columns
35831         var i = this.cols;
35832         while ( --i ) {
35833           if ( this.colYs[i] !== 0 ) {
35834             break;
35835           }
35836           unusedCols++;
35837         }
35838         // fit container to columns that have been used
35839         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35840     },
35841     
35842     needsResizeLayout : function()
35843     {
35844         var previousWidth = this.containerWidth;
35845         this.getContainerWidth();
35846         return previousWidth !== this.containerWidth;
35847     }
35848  
35849 });
35850
35851  
35852
35853  /*
35854  * - LGPL
35855  *
35856  * element
35857  * 
35858  */
35859
35860 /**
35861  * @class Roo.bootstrap.MasonryBrick
35862  * @extends Roo.bootstrap.Component
35863  * Bootstrap MasonryBrick class
35864  * 
35865  * @constructor
35866  * Create a new MasonryBrick
35867  * @param {Object} config The config object
35868  */
35869
35870 Roo.bootstrap.MasonryBrick = function(config){
35871     
35872     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35873     
35874     Roo.bootstrap.MasonryBrick.register(this);
35875     
35876     this.addEvents({
35877         // raw events
35878         /**
35879          * @event click
35880          * When a MasonryBrick is clcik
35881          * @param {Roo.bootstrap.MasonryBrick} this
35882          * @param {Roo.EventObject} e
35883          */
35884         "click" : true
35885     });
35886 };
35887
35888 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35889     
35890     /**
35891      * @cfg {String} title
35892      */   
35893     title : '',
35894     /**
35895      * @cfg {String} html
35896      */   
35897     html : '',
35898     /**
35899      * @cfg {String} bgimage
35900      */   
35901     bgimage : '',
35902     /**
35903      * @cfg {String} videourl
35904      */   
35905     videourl : '',
35906     /**
35907      * @cfg {String} cls
35908      */   
35909     cls : '',
35910     /**
35911      * @cfg {String} href
35912      */   
35913     href : '',
35914     /**
35915      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35916      */   
35917     size : 'xs',
35918     
35919     /**
35920      * @cfg {String} placetitle (center|bottom)
35921      */   
35922     placetitle : '',
35923     
35924     /**
35925      * @cfg {Boolean} isFitContainer defalut true
35926      */   
35927     isFitContainer : true, 
35928     
35929     /**
35930      * @cfg {Boolean} preventDefault defalut false
35931      */   
35932     preventDefault : false, 
35933     
35934     /**
35935      * @cfg {Boolean} inverse defalut false
35936      */   
35937     maskInverse : false, 
35938     
35939     getAutoCreate : function()
35940     {
35941         if(!this.isFitContainer){
35942             return this.getSplitAutoCreate();
35943         }
35944         
35945         var cls = 'masonry-brick masonry-brick-full';
35946         
35947         if(this.href.length){
35948             cls += ' masonry-brick-link';
35949         }
35950         
35951         if(this.bgimage.length){
35952             cls += ' masonry-brick-image';
35953         }
35954         
35955         if(this.maskInverse){
35956             cls += ' mask-inverse';
35957         }
35958         
35959         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35960             cls += ' enable-mask';
35961         }
35962         
35963         if(this.size){
35964             cls += ' masonry-' + this.size + '-brick';
35965         }
35966         
35967         if(this.placetitle.length){
35968             
35969             switch (this.placetitle) {
35970                 case 'center' :
35971                     cls += ' masonry-center-title';
35972                     break;
35973                 case 'bottom' :
35974                     cls += ' masonry-bottom-title';
35975                     break;
35976                 default:
35977                     break;
35978             }
35979             
35980         } else {
35981             if(!this.html.length && !this.bgimage.length){
35982                 cls += ' masonry-center-title';
35983             }
35984
35985             if(!this.html.length && this.bgimage.length){
35986                 cls += ' masonry-bottom-title';
35987             }
35988         }
35989         
35990         if(this.cls){
35991             cls += ' ' + this.cls;
35992         }
35993         
35994         var cfg = {
35995             tag: (this.href.length) ? 'a' : 'div',
35996             cls: cls,
35997             cn: [
35998                 {
35999                     tag: 'div',
36000                     cls: 'masonry-brick-mask'
36001                 },
36002                 {
36003                     tag: 'div',
36004                     cls: 'masonry-brick-paragraph',
36005                     cn: []
36006                 }
36007             ]
36008         };
36009         
36010         if(this.href.length){
36011             cfg.href = this.href;
36012         }
36013         
36014         var cn = cfg.cn[1].cn;
36015         
36016         if(this.title.length){
36017             cn.push({
36018                 tag: 'h4',
36019                 cls: 'masonry-brick-title',
36020                 html: this.title
36021             });
36022         }
36023         
36024         if(this.html.length){
36025             cn.push({
36026                 tag: 'p',
36027                 cls: 'masonry-brick-text',
36028                 html: this.html
36029             });
36030         }
36031         
36032         if (!this.title.length && !this.html.length) {
36033             cfg.cn[1].cls += ' hide';
36034         }
36035         
36036         if(this.bgimage.length){
36037             cfg.cn.push({
36038                 tag: 'img',
36039                 cls: 'masonry-brick-image-view',
36040                 src: this.bgimage
36041             });
36042         }
36043         
36044         if(this.videourl.length){
36045             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36046             // youtube support only?
36047             cfg.cn.push({
36048                 tag: 'iframe',
36049                 cls: 'masonry-brick-image-view',
36050                 src: vurl,
36051                 frameborder : 0,
36052                 allowfullscreen : true
36053             });
36054         }
36055         
36056         return cfg;
36057         
36058     },
36059     
36060     getSplitAutoCreate : function()
36061     {
36062         var cls = 'masonry-brick masonry-brick-split';
36063         
36064         if(this.href.length){
36065             cls += ' masonry-brick-link';
36066         }
36067         
36068         if(this.bgimage.length){
36069             cls += ' masonry-brick-image';
36070         }
36071         
36072         if(this.size){
36073             cls += ' masonry-' + this.size + '-brick';
36074         }
36075         
36076         switch (this.placetitle) {
36077             case 'center' :
36078                 cls += ' masonry-center-title';
36079                 break;
36080             case 'bottom' :
36081                 cls += ' masonry-bottom-title';
36082                 break;
36083             default:
36084                 if(!this.bgimage.length){
36085                     cls += ' masonry-center-title';
36086                 }
36087
36088                 if(this.bgimage.length){
36089                     cls += ' masonry-bottom-title';
36090                 }
36091                 break;
36092         }
36093         
36094         if(this.cls){
36095             cls += ' ' + this.cls;
36096         }
36097         
36098         var cfg = {
36099             tag: (this.href.length) ? 'a' : 'div',
36100             cls: cls,
36101             cn: [
36102                 {
36103                     tag: 'div',
36104                     cls: 'masonry-brick-split-head',
36105                     cn: [
36106                         {
36107                             tag: 'div',
36108                             cls: 'masonry-brick-paragraph',
36109                             cn: []
36110                         }
36111                     ]
36112                 },
36113                 {
36114                     tag: 'div',
36115                     cls: 'masonry-brick-split-body',
36116                     cn: []
36117                 }
36118             ]
36119         };
36120         
36121         if(this.href.length){
36122             cfg.href = this.href;
36123         }
36124         
36125         if(this.title.length){
36126             cfg.cn[0].cn[0].cn.push({
36127                 tag: 'h4',
36128                 cls: 'masonry-brick-title',
36129                 html: this.title
36130             });
36131         }
36132         
36133         if(this.html.length){
36134             cfg.cn[1].cn.push({
36135                 tag: 'p',
36136                 cls: 'masonry-brick-text',
36137                 html: this.html
36138             });
36139         }
36140
36141         if(this.bgimage.length){
36142             cfg.cn[0].cn.push({
36143                 tag: 'img',
36144                 cls: 'masonry-brick-image-view',
36145                 src: this.bgimage
36146             });
36147         }
36148         
36149         if(this.videourl.length){
36150             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36151             // youtube support only?
36152             cfg.cn[0].cn.cn.push({
36153                 tag: 'iframe',
36154                 cls: 'masonry-brick-image-view',
36155                 src: vurl,
36156                 frameborder : 0,
36157                 allowfullscreen : true
36158             });
36159         }
36160         
36161         return cfg;
36162     },
36163     
36164     initEvents: function() 
36165     {
36166         switch (this.size) {
36167             case 'xs' :
36168                 this.x = 1;
36169                 this.y = 1;
36170                 break;
36171             case 'sm' :
36172                 this.x = 2;
36173                 this.y = 2;
36174                 break;
36175             case 'md' :
36176             case 'md-left' :
36177             case 'md-right' :
36178                 this.x = 3;
36179                 this.y = 3;
36180                 break;
36181             case 'tall' :
36182                 this.x = 2;
36183                 this.y = 3;
36184                 break;
36185             case 'wide' :
36186                 this.x = 3;
36187                 this.y = 2;
36188                 break;
36189             case 'wide-thin' :
36190                 this.x = 3;
36191                 this.y = 1;
36192                 break;
36193                         
36194             default :
36195                 break;
36196         }
36197         
36198         if(Roo.isTouch){
36199             this.el.on('touchstart', this.onTouchStart, this);
36200             this.el.on('touchmove', this.onTouchMove, this);
36201             this.el.on('touchend', this.onTouchEnd, this);
36202             this.el.on('contextmenu', this.onContextMenu, this);
36203         } else {
36204             this.el.on('mouseenter'  ,this.enter, this);
36205             this.el.on('mouseleave', this.leave, this);
36206             this.el.on('click', this.onClick, this);
36207         }
36208         
36209         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36210             this.parent().bricks.push(this);   
36211         }
36212         
36213     },
36214     
36215     onClick: function(e, el)
36216     {
36217         var time = this.endTimer - this.startTimer;
36218         // Roo.log(e.preventDefault());
36219         if(Roo.isTouch){
36220             if(time > 1000){
36221                 e.preventDefault();
36222                 return;
36223             }
36224         }
36225         
36226         if(!this.preventDefault){
36227             return;
36228         }
36229         
36230         e.preventDefault();
36231         
36232         if (this.activeClass != '') {
36233             this.selectBrick();
36234         }
36235         
36236         this.fireEvent('click', this, e);
36237     },
36238     
36239     enter: function(e, el)
36240     {
36241         e.preventDefault();
36242         
36243         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36244             return;
36245         }
36246         
36247         if(this.bgimage.length && this.html.length){
36248             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36249         }
36250     },
36251     
36252     leave: function(e, el)
36253     {
36254         e.preventDefault();
36255         
36256         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36257             return;
36258         }
36259         
36260         if(this.bgimage.length && this.html.length){
36261             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36262         }
36263     },
36264     
36265     onTouchStart: function(e, el)
36266     {
36267 //        e.preventDefault();
36268         
36269         this.touchmoved = false;
36270         
36271         if(!this.isFitContainer){
36272             return;
36273         }
36274         
36275         if(!this.bgimage.length || !this.html.length){
36276             return;
36277         }
36278         
36279         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36280         
36281         this.timer = new Date().getTime();
36282         
36283     },
36284     
36285     onTouchMove: function(e, el)
36286     {
36287         this.touchmoved = true;
36288     },
36289     
36290     onContextMenu : function(e,el)
36291     {
36292         e.preventDefault();
36293         e.stopPropagation();
36294         return false;
36295     },
36296     
36297     onTouchEnd: function(e, el)
36298     {
36299 //        e.preventDefault();
36300         
36301         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36302         
36303             this.leave(e,el);
36304             
36305             return;
36306         }
36307         
36308         if(!this.bgimage.length || !this.html.length){
36309             
36310             if(this.href.length){
36311                 window.location.href = this.href;
36312             }
36313             
36314             return;
36315         }
36316         
36317         if(!this.isFitContainer){
36318             return;
36319         }
36320         
36321         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36322         
36323         window.location.href = this.href;
36324     },
36325     
36326     //selection on single brick only
36327     selectBrick : function() {
36328         
36329         if (!this.parentId) {
36330             return;
36331         }
36332         
36333         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36334         var index = m.selectedBrick.indexOf(this.id);
36335         
36336         if ( index > -1) {
36337             m.selectedBrick.splice(index,1);
36338             this.el.removeClass(this.activeClass);
36339             return;
36340         }
36341         
36342         for(var i = 0; i < m.selectedBrick.length; i++) {
36343             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36344             b.el.removeClass(b.activeClass);
36345         }
36346         
36347         m.selectedBrick = [];
36348         
36349         m.selectedBrick.push(this.id);
36350         this.el.addClass(this.activeClass);
36351         return;
36352     },
36353     
36354     isSelected : function(){
36355         return this.el.hasClass(this.activeClass);
36356         
36357     }
36358 });
36359
36360 Roo.apply(Roo.bootstrap.MasonryBrick, {
36361     
36362     //groups: {},
36363     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36364      /**
36365     * register a Masonry Brick
36366     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36367     */
36368     
36369     register : function(brick)
36370     {
36371         //this.groups[brick.id] = brick;
36372         this.groups.add(brick.id, brick);
36373     },
36374     /**
36375     * fetch a  masonry brick based on the masonry brick ID
36376     * @param {string} the masonry brick to add
36377     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36378     */
36379     
36380     get: function(brick_id) 
36381     {
36382         // if (typeof(this.groups[brick_id]) == 'undefined') {
36383         //     return false;
36384         // }
36385         // return this.groups[brick_id] ;
36386         
36387         if(this.groups.key(brick_id)) {
36388             return this.groups.key(brick_id);
36389         }
36390         
36391         return false;
36392     }
36393     
36394     
36395     
36396 });
36397
36398  /*
36399  * - LGPL
36400  *
36401  * element
36402  * 
36403  */
36404
36405 /**
36406  * @class Roo.bootstrap.Brick
36407  * @extends Roo.bootstrap.Component
36408  * Bootstrap Brick class
36409  * 
36410  * @constructor
36411  * Create a new Brick
36412  * @param {Object} config The config object
36413  */
36414
36415 Roo.bootstrap.Brick = function(config){
36416     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36417     
36418     this.addEvents({
36419         // raw events
36420         /**
36421          * @event click
36422          * When a Brick is click
36423          * @param {Roo.bootstrap.Brick} this
36424          * @param {Roo.EventObject} e
36425          */
36426         "click" : true
36427     });
36428 };
36429
36430 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36431     
36432     /**
36433      * @cfg {String} title
36434      */   
36435     title : '',
36436     /**
36437      * @cfg {String} html
36438      */   
36439     html : '',
36440     /**
36441      * @cfg {String} bgimage
36442      */   
36443     bgimage : '',
36444     /**
36445      * @cfg {String} cls
36446      */   
36447     cls : '',
36448     /**
36449      * @cfg {String} href
36450      */   
36451     href : '',
36452     /**
36453      * @cfg {String} video
36454      */   
36455     video : '',
36456     /**
36457      * @cfg {Boolean} square
36458      */   
36459     square : true,
36460     
36461     getAutoCreate : function()
36462     {
36463         var cls = 'roo-brick';
36464         
36465         if(this.href.length){
36466             cls += ' roo-brick-link';
36467         }
36468         
36469         if(this.bgimage.length){
36470             cls += ' roo-brick-image';
36471         }
36472         
36473         if(!this.html.length && !this.bgimage.length){
36474             cls += ' roo-brick-center-title';
36475         }
36476         
36477         if(!this.html.length && this.bgimage.length){
36478             cls += ' roo-brick-bottom-title';
36479         }
36480         
36481         if(this.cls){
36482             cls += ' ' + this.cls;
36483         }
36484         
36485         var cfg = {
36486             tag: (this.href.length) ? 'a' : 'div',
36487             cls: cls,
36488             cn: [
36489                 {
36490                     tag: 'div',
36491                     cls: 'roo-brick-paragraph',
36492                     cn: []
36493                 }
36494             ]
36495         };
36496         
36497         if(this.href.length){
36498             cfg.href = this.href;
36499         }
36500         
36501         var cn = cfg.cn[0].cn;
36502         
36503         if(this.title.length){
36504             cn.push({
36505                 tag: 'h4',
36506                 cls: 'roo-brick-title',
36507                 html: this.title
36508             });
36509         }
36510         
36511         if(this.html.length){
36512             cn.push({
36513                 tag: 'p',
36514                 cls: 'roo-brick-text',
36515                 html: this.html
36516             });
36517         } else {
36518             cn.cls += ' hide';
36519         }
36520         
36521         if(this.bgimage.length){
36522             cfg.cn.push({
36523                 tag: 'img',
36524                 cls: 'roo-brick-image-view',
36525                 src: this.bgimage
36526             });
36527         }
36528         
36529         return cfg;
36530     },
36531     
36532     initEvents: function() 
36533     {
36534         if(this.title.length || this.html.length){
36535             this.el.on('mouseenter'  ,this.enter, this);
36536             this.el.on('mouseleave', this.leave, this);
36537         }
36538         
36539         Roo.EventManager.onWindowResize(this.resize, this); 
36540         
36541         if(this.bgimage.length){
36542             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36543             this.imageEl.on('load', this.onImageLoad, this);
36544             return;
36545         }
36546         
36547         this.resize();
36548     },
36549     
36550     onImageLoad : function()
36551     {
36552         this.resize();
36553     },
36554     
36555     resize : function()
36556     {
36557         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36558         
36559         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36560         
36561         if(this.bgimage.length){
36562             var image = this.el.select('.roo-brick-image-view', true).first();
36563             
36564             image.setWidth(paragraph.getWidth());
36565             
36566             if(this.square){
36567                 image.setHeight(paragraph.getWidth());
36568             }
36569             
36570             this.el.setHeight(image.getHeight());
36571             paragraph.setHeight(image.getHeight());
36572             
36573         }
36574         
36575     },
36576     
36577     enter: function(e, el)
36578     {
36579         e.preventDefault();
36580         
36581         if(this.bgimage.length){
36582             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36583             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36584         }
36585     },
36586     
36587     leave: function(e, el)
36588     {
36589         e.preventDefault();
36590         
36591         if(this.bgimage.length){
36592             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36593             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36594         }
36595     }
36596     
36597 });
36598
36599  
36600
36601  /*
36602  * - LGPL
36603  *
36604  * Number field 
36605  */
36606
36607 /**
36608  * @class Roo.bootstrap.form.NumberField
36609  * @extends Roo.bootstrap.form.Input
36610  * Bootstrap NumberField class
36611  * 
36612  * 
36613  * 
36614  * 
36615  * @constructor
36616  * Create a new NumberField
36617  * @param {Object} config The config object
36618  */
36619
36620 Roo.bootstrap.form.NumberField = function(config){
36621     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36622 };
36623
36624 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36625     
36626     /**
36627      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36628      */
36629     allowDecimals : true,
36630     /**
36631      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36632      */
36633     decimalSeparator : ".",
36634     /**
36635      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36636      */
36637     decimalPrecision : 2,
36638     /**
36639      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36640      */
36641     allowNegative : true,
36642     
36643     /**
36644      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36645      */
36646     allowZero: true,
36647     /**
36648      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36649      */
36650     minValue : Number.NEGATIVE_INFINITY,
36651     /**
36652      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36653      */
36654     maxValue : Number.MAX_VALUE,
36655     /**
36656      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36657      */
36658     minText : "The minimum value for this field is {0}",
36659     /**
36660      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36661      */
36662     maxText : "The maximum value for this field is {0}",
36663     /**
36664      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36665      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36666      */
36667     nanText : "{0} is not a valid number",
36668     /**
36669      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36670      */
36671     thousandsDelimiter : false,
36672     /**
36673      * @cfg {String} valueAlign alignment of value
36674      */
36675     valueAlign : "left",
36676
36677     getAutoCreate : function()
36678     {
36679         var hiddenInput = {
36680             tag: 'input',
36681             type: 'hidden',
36682             id: Roo.id(),
36683             cls: 'hidden-number-input'
36684         };
36685         
36686         if (this.name) {
36687             hiddenInput.name = this.name;
36688         }
36689         
36690         this.name = '';
36691         
36692         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36693         
36694         this.name = hiddenInput.name;
36695         
36696         if(cfg.cn.length > 0) {
36697             cfg.cn.push(hiddenInput);
36698         }
36699         
36700         return cfg;
36701     },
36702
36703     // private
36704     initEvents : function()
36705     {   
36706         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36707         
36708         var allowed = "0123456789";
36709         
36710         if(this.allowDecimals){
36711             allowed += this.decimalSeparator;
36712         }
36713         
36714         if(this.allowNegative){
36715             allowed += "-";
36716         }
36717         
36718         if(this.thousandsDelimiter) {
36719             allowed += ",";
36720         }
36721         
36722         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36723         
36724         var keyPress = function(e){
36725             
36726             var k = e.getKey();
36727             
36728             var c = e.getCharCode();
36729             
36730             if(
36731                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36732                     allowed.indexOf(String.fromCharCode(c)) === -1
36733             ){
36734                 e.stopEvent();
36735                 return;
36736             }
36737             
36738             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36739                 return;
36740             }
36741             
36742             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36743                 e.stopEvent();
36744             }
36745         };
36746         
36747         this.el.on("keypress", keyPress, this);
36748     },
36749     
36750     validateValue : function(value)
36751     {
36752         
36753         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36754             return false;
36755         }
36756         
36757         var num = this.parseValue(value);
36758         
36759         if(isNaN(num)){
36760             this.markInvalid(String.format(this.nanText, value));
36761             return false;
36762         }
36763         
36764         if(num < this.minValue){
36765             this.markInvalid(String.format(this.minText, this.minValue));
36766             return false;
36767         }
36768         
36769         if(num > this.maxValue){
36770             this.markInvalid(String.format(this.maxText, this.maxValue));
36771             return false;
36772         }
36773         
36774         return true;
36775     },
36776
36777     getValue : function()
36778     {
36779         var v = this.hiddenEl().getValue();
36780         
36781         return this.fixPrecision(this.parseValue(v));
36782     },
36783
36784     parseValue : function(value)
36785     {
36786         if(this.thousandsDelimiter) {
36787             value += "";
36788             r = new RegExp(",", "g");
36789             value = value.replace(r, "");
36790         }
36791         
36792         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36793         return isNaN(value) ? '' : value;
36794     },
36795
36796     fixPrecision : function(value)
36797     {
36798         if(this.thousandsDelimiter) {
36799             value += "";
36800             r = new RegExp(",", "g");
36801             value = value.replace(r, "");
36802         }
36803         
36804         var nan = isNaN(value);
36805         
36806         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36807             return nan ? '' : value;
36808         }
36809         return parseFloat(value).toFixed(this.decimalPrecision);
36810     },
36811
36812     setValue : function(v)
36813     {
36814         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36815         
36816         this.value = v;
36817         
36818         if(this.rendered){
36819             
36820             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36821             
36822             this.inputEl().dom.value = (v == '') ? '' :
36823                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36824             
36825             if(!this.allowZero && v === '0') {
36826                 this.hiddenEl().dom.value = '';
36827                 this.inputEl().dom.value = '';
36828             }
36829             
36830             this.validate();
36831         }
36832     },
36833
36834     decimalPrecisionFcn : function(v)
36835     {
36836         return Math.floor(v);
36837     },
36838
36839     beforeBlur : function()
36840     {
36841         var v = this.parseValue(this.getRawValue());
36842         
36843         if(v || v === 0 || v === ''){
36844             this.setValue(v);
36845         }
36846     },
36847     
36848     hiddenEl : function()
36849     {
36850         return this.el.select('input.hidden-number-input',true).first();
36851     }
36852     
36853 });
36854
36855  
36856
36857 /*
36858 * Licence: LGPL
36859 */
36860
36861 /**
36862  * @class Roo.bootstrap.DocumentSlider
36863  * @extends Roo.bootstrap.Component
36864  * Bootstrap DocumentSlider class
36865  * 
36866  * @constructor
36867  * Create a new DocumentViewer
36868  * @param {Object} config The config object
36869  */
36870
36871 Roo.bootstrap.DocumentSlider = function(config){
36872     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36873     
36874     this.files = [];
36875     
36876     this.addEvents({
36877         /**
36878          * @event initial
36879          * Fire after initEvent
36880          * @param {Roo.bootstrap.DocumentSlider} this
36881          */
36882         "initial" : true,
36883         /**
36884          * @event update
36885          * Fire after update
36886          * @param {Roo.bootstrap.DocumentSlider} this
36887          */
36888         "update" : true,
36889         /**
36890          * @event click
36891          * Fire after click
36892          * @param {Roo.bootstrap.DocumentSlider} this
36893          */
36894         "click" : true
36895     });
36896 };
36897
36898 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36899     
36900     files : false,
36901     
36902     indicator : 0,
36903     
36904     getAutoCreate : function()
36905     {
36906         var cfg = {
36907             tag : 'div',
36908             cls : 'roo-document-slider',
36909             cn : [
36910                 {
36911                     tag : 'div',
36912                     cls : 'roo-document-slider-header',
36913                     cn : [
36914                         {
36915                             tag : 'div',
36916                             cls : 'roo-document-slider-header-title'
36917                         }
36918                     ]
36919                 },
36920                 {
36921                     tag : 'div',
36922                     cls : 'roo-document-slider-body',
36923                     cn : [
36924                         {
36925                             tag : 'div',
36926                             cls : 'roo-document-slider-prev',
36927                             cn : [
36928                                 {
36929                                     tag : 'i',
36930                                     cls : 'fa fa-chevron-left'
36931                                 }
36932                             ]
36933                         },
36934                         {
36935                             tag : 'div',
36936                             cls : 'roo-document-slider-thumb',
36937                             cn : [
36938                                 {
36939                                     tag : 'img',
36940                                     cls : 'roo-document-slider-image'
36941                                 }
36942                             ]
36943                         },
36944                         {
36945                             tag : 'div',
36946                             cls : 'roo-document-slider-next',
36947                             cn : [
36948                                 {
36949                                     tag : 'i',
36950                                     cls : 'fa fa-chevron-right'
36951                                 }
36952                             ]
36953                         }
36954                     ]
36955                 }
36956             ]
36957         };
36958         
36959         return cfg;
36960     },
36961     
36962     initEvents : function()
36963     {
36964         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36965         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36966         
36967         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36968         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36969         
36970         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36971         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36972         
36973         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36974         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36975         
36976         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36977         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36978         
36979         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36980         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36981         
36982         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36983         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36984         
36985         this.thumbEl.on('click', this.onClick, this);
36986         
36987         this.prevIndicator.on('click', this.prev, this);
36988         
36989         this.nextIndicator.on('click', this.next, this);
36990         
36991     },
36992     
36993     initial : function()
36994     {
36995         if(this.files.length){
36996             this.indicator = 1;
36997             this.update()
36998         }
36999         
37000         this.fireEvent('initial', this);
37001     },
37002     
37003     update : function()
37004     {
37005         this.imageEl.attr('src', this.files[this.indicator - 1]);
37006         
37007         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37008         
37009         this.prevIndicator.show();
37010         
37011         if(this.indicator == 1){
37012             this.prevIndicator.hide();
37013         }
37014         
37015         this.nextIndicator.show();
37016         
37017         if(this.indicator == this.files.length){
37018             this.nextIndicator.hide();
37019         }
37020         
37021         this.thumbEl.scrollTo('top');
37022         
37023         this.fireEvent('update', this);
37024     },
37025     
37026     onClick : function(e)
37027     {
37028         e.preventDefault();
37029         
37030         this.fireEvent('click', this);
37031     },
37032     
37033     prev : function(e)
37034     {
37035         e.preventDefault();
37036         
37037         this.indicator = Math.max(1, this.indicator - 1);
37038         
37039         this.update();
37040     },
37041     
37042     next : function(e)
37043     {
37044         e.preventDefault();
37045         
37046         this.indicator = Math.min(this.files.length, this.indicator + 1);
37047         
37048         this.update();
37049     }
37050 });
37051 /*
37052  * - LGPL
37053  *
37054  * RadioSet
37055  *
37056  *
37057  */
37058
37059 /**
37060  * @class Roo.bootstrap.form.RadioSet
37061  * @extends Roo.bootstrap.form.Input
37062  * @children Roo.bootstrap.form.Radio
37063  * Bootstrap RadioSet class
37064  * @cfg {String} indicatorpos (left|right) default left
37065  * @cfg {Boolean} inline (true|false) inline the element (default true)
37066  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37067  * @constructor
37068  * Create a new RadioSet
37069  * @param {Object} config The config object
37070  */
37071
37072 Roo.bootstrap.form.RadioSet = function(config){
37073     
37074     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37075     
37076     this.radioes = [];
37077     
37078     Roo.bootstrap.form.RadioSet.register(this);
37079     
37080     this.addEvents({
37081         /**
37082         * @event check
37083         * Fires when the element is checked or unchecked.
37084         * @param {Roo.bootstrap.form.RadioSet} this This radio
37085         * @param {Roo.bootstrap.form.Radio} item The checked item
37086         */
37087        check : true,
37088        /**
37089         * @event click
37090         * Fires when the element is click.
37091         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37092         * @param {Roo.bootstrap.form.Radio} item The checked item
37093         * @param {Roo.EventObject} e The event object
37094         */
37095        click : true
37096     });
37097     
37098 };
37099
37100 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37101
37102     radioes : false,
37103     
37104     inline : true,
37105     
37106     weight : '',
37107     
37108     indicatorpos : 'left',
37109     
37110     getAutoCreate : function()
37111     {
37112         var label = {
37113             tag : 'label',
37114             cls : 'roo-radio-set-label',
37115             cn : [
37116                 {
37117                     tag : 'span',
37118                     html : this.fieldLabel
37119                 }
37120             ]
37121         };
37122         if (Roo.bootstrap.version == 3) {
37123             
37124             
37125             if(this.indicatorpos == 'left'){
37126                 label.cn.unshift({
37127                     tag : 'i',
37128                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37129                     tooltip : 'This field is required'
37130                 });
37131             } else {
37132                 label.cn.push({
37133                     tag : 'i',
37134                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37135                     tooltip : 'This field is required'
37136                 });
37137             }
37138         }
37139         var items = {
37140             tag : 'div',
37141             cls : 'roo-radio-set-items'
37142         };
37143         
37144         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37145         
37146         if (align === 'left' && this.fieldLabel.length) {
37147             
37148             items = {
37149                 cls : "roo-radio-set-right", 
37150                 cn: [
37151                     items
37152                 ]
37153             };
37154             
37155             if(this.labelWidth > 12){
37156                 label.style = "width: " + this.labelWidth + 'px';
37157             }
37158             
37159             if(this.labelWidth < 13 && this.labelmd == 0){
37160                 this.labelmd = this.labelWidth;
37161             }
37162             
37163             if(this.labellg > 0){
37164                 label.cls += ' col-lg-' + this.labellg;
37165                 items.cls += ' col-lg-' + (12 - this.labellg);
37166             }
37167             
37168             if(this.labelmd > 0){
37169                 label.cls += ' col-md-' + this.labelmd;
37170                 items.cls += ' col-md-' + (12 - this.labelmd);
37171             }
37172             
37173             if(this.labelsm > 0){
37174                 label.cls += ' col-sm-' + this.labelsm;
37175                 items.cls += ' col-sm-' + (12 - this.labelsm);
37176             }
37177             
37178             if(this.labelxs > 0){
37179                 label.cls += ' col-xs-' + this.labelxs;
37180                 items.cls += ' col-xs-' + (12 - this.labelxs);
37181             }
37182         }
37183         
37184         var cfg = {
37185             tag : 'div',
37186             cls : 'roo-radio-set',
37187             cn : [
37188                 {
37189                     tag : 'input',
37190                     cls : 'roo-radio-set-input',
37191                     type : 'hidden',
37192                     name : this.name,
37193                     value : this.value ? this.value :  ''
37194                 },
37195                 label,
37196                 items
37197             ]
37198         };
37199         
37200         if(this.weight.length){
37201             cfg.cls += ' roo-radio-' + this.weight;
37202         }
37203         
37204         if(this.inline) {
37205             cfg.cls += ' roo-radio-set-inline';
37206         }
37207         
37208         var settings=this;
37209         ['xs','sm','md','lg'].map(function(size){
37210             if (settings[size]) {
37211                 cfg.cls += ' col-' + size + '-' + settings[size];
37212             }
37213         });
37214         
37215         return cfg;
37216         
37217     },
37218
37219     initEvents : function()
37220     {
37221         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37222         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37223         
37224         if(!this.fieldLabel.length){
37225             this.labelEl.hide();
37226         }
37227         
37228         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37229         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37230         
37231         this.indicator = this.indicatorEl();
37232         
37233         if(this.indicator){
37234             this.indicator.addClass('invisible');
37235         }
37236         
37237         this.originalValue = this.getValue();
37238         
37239     },
37240     
37241     inputEl: function ()
37242     {
37243         return this.el.select('.roo-radio-set-input', true).first();
37244     },
37245     
37246     getChildContainer : function()
37247     {
37248         return this.itemsEl;
37249     },
37250     
37251     register : function(item)
37252     {
37253         this.radioes.push(item);
37254         
37255     },
37256     
37257     validate : function()
37258     {   
37259         if(this.getVisibilityEl().hasClass('hidden')){
37260             return true;
37261         }
37262         
37263         var valid = false;
37264         
37265         Roo.each(this.radioes, function(i){
37266             if(!i.checked){
37267                 return;
37268             }
37269             
37270             valid = true;
37271             return false;
37272         });
37273         
37274         if(this.allowBlank) {
37275             return true;
37276         }
37277         
37278         if(this.disabled || valid){
37279             this.markValid();
37280             return true;
37281         }
37282         
37283         this.markInvalid();
37284         return false;
37285         
37286     },
37287     
37288     markValid : function()
37289     {
37290         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37291             this.indicatorEl().removeClass('visible');
37292             this.indicatorEl().addClass('invisible');
37293         }
37294         
37295         
37296         if (Roo.bootstrap.version == 3) {
37297             this.el.removeClass([this.invalidClass, this.validClass]);
37298             this.el.addClass(this.validClass);
37299         } else {
37300             this.el.removeClass(['is-invalid','is-valid']);
37301             this.el.addClass(['is-valid']);
37302         }
37303         this.fireEvent('valid', this);
37304     },
37305     
37306     markInvalid : function(msg)
37307     {
37308         if(this.allowBlank || this.disabled){
37309             return;
37310         }
37311         
37312         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37313             this.indicatorEl().removeClass('invisible');
37314             this.indicatorEl().addClass('visible');
37315         }
37316         if (Roo.bootstrap.version == 3) {
37317             this.el.removeClass([this.invalidClass, this.validClass]);
37318             this.el.addClass(this.invalidClass);
37319         } else {
37320             this.el.removeClass(['is-invalid','is-valid']);
37321             this.el.addClass(['is-invalid']);
37322         }
37323         
37324         this.fireEvent('invalid', this, msg);
37325         
37326     },
37327     
37328     setValue : function(v, suppressEvent)
37329     {   
37330         if(this.value === v){
37331             return;
37332         }
37333         
37334         this.value = v;
37335         
37336         if(this.rendered){
37337             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37338         }
37339         
37340         Roo.each(this.radioes, function(i){
37341             i.checked = false;
37342             i.el.removeClass('checked');
37343         });
37344         
37345         Roo.each(this.radioes, function(i){
37346             
37347             if(i.value === v || i.value.toString() === v.toString()){
37348                 i.checked = true;
37349                 i.el.addClass('checked');
37350                 
37351                 if(suppressEvent !== true){
37352                     this.fireEvent('check', this, i);
37353                 }
37354                 
37355                 return false;
37356             }
37357             
37358         }, this);
37359         
37360         this.validate();
37361     },
37362     
37363     clearInvalid : function(){
37364         
37365         if(!this.el || this.preventMark){
37366             return;
37367         }
37368         
37369         this.el.removeClass([this.invalidClass]);
37370         
37371         this.fireEvent('valid', this);
37372     }
37373     
37374 });
37375
37376 Roo.apply(Roo.bootstrap.form.RadioSet, {
37377     
37378     groups: {},
37379     
37380     register : function(set)
37381     {
37382         this.groups[set.name] = set;
37383     },
37384     
37385     get: function(name) 
37386     {
37387         if (typeof(this.groups[name]) == 'undefined') {
37388             return false;
37389         }
37390         
37391         return this.groups[name] ;
37392     }
37393     
37394 });
37395 /*
37396  * Based on:
37397  * Ext JS Library 1.1.1
37398  * Copyright(c) 2006-2007, Ext JS, LLC.
37399  *
37400  * Originally Released Under LGPL - original licence link has changed is not relivant.
37401  *
37402  * Fork - LGPL
37403  * <script type="text/javascript">
37404  */
37405
37406
37407 /**
37408  * @class Roo.bootstrap.SplitBar
37409  * @extends Roo.util.Observable
37410  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37411  * <br><br>
37412  * Usage:
37413  * <pre><code>
37414 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37415                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37416 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37417 split.minSize = 100;
37418 split.maxSize = 600;
37419 split.animate = true;
37420 split.on('moved', splitterMoved);
37421 </code></pre>
37422  * @constructor
37423  * Create a new SplitBar
37424  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37425  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37426  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37427  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37428                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37429                         position of the SplitBar).
37430  */
37431 Roo.bootstrap.SplitBar = function(cfg){
37432     
37433     /** @private */
37434     
37435     //{
37436     //  dragElement : elm
37437     //  resizingElement: el,
37438         // optional..
37439     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37440     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37441         // existingProxy ???
37442     //}
37443     
37444     this.el = Roo.get(cfg.dragElement, true);
37445     this.el.dom.unselectable = "on";
37446     /** @private */
37447     this.resizingEl = Roo.get(cfg.resizingElement, true);
37448
37449     /**
37450      * @private
37451      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37452      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37453      * @type Number
37454      */
37455     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37456     
37457     /**
37458      * The minimum size of the resizing element. (Defaults to 0)
37459      * @type Number
37460      */
37461     this.minSize = 0;
37462     
37463     /**
37464      * The maximum size of the resizing element. (Defaults to 2000)
37465      * @type Number
37466      */
37467     this.maxSize = 2000;
37468     
37469     /**
37470      * Whether to animate the transition to the new size
37471      * @type Boolean
37472      */
37473     this.animate = false;
37474     
37475     /**
37476      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37477      * @type Boolean
37478      */
37479     this.useShim = false;
37480     
37481     /** @private */
37482     this.shim = null;
37483     
37484     if(!cfg.existingProxy){
37485         /** @private */
37486         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37487     }else{
37488         this.proxy = Roo.get(cfg.existingProxy).dom;
37489     }
37490     /** @private */
37491     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37492     
37493     /** @private */
37494     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37495     
37496     /** @private */
37497     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37498     
37499     /** @private */
37500     this.dragSpecs = {};
37501     
37502     /**
37503      * @private The adapter to use to positon and resize elements
37504      */
37505     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37506     this.adapter.init(this);
37507     
37508     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37509         /** @private */
37510         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37511         this.el.addClass("roo-splitbar-h");
37512     }else{
37513         /** @private */
37514         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37515         this.el.addClass("roo-splitbar-v");
37516     }
37517     
37518     this.addEvents({
37519         /**
37520          * @event resize
37521          * Fires when the splitter is moved (alias for {@link #event-moved})
37522          * @param {Roo.bootstrap.SplitBar} this
37523          * @param {Number} newSize the new width or height
37524          */
37525         "resize" : true,
37526         /**
37527          * @event moved
37528          * Fires when the splitter is moved
37529          * @param {Roo.bootstrap.SplitBar} this
37530          * @param {Number} newSize the new width or height
37531          */
37532         "moved" : true,
37533         /**
37534          * @event beforeresize
37535          * Fires before the splitter is dragged
37536          * @param {Roo.bootstrap.SplitBar} this
37537          */
37538         "beforeresize" : true,
37539
37540         "beforeapply" : true
37541     });
37542
37543     Roo.util.Observable.call(this);
37544 };
37545
37546 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37547     onStartProxyDrag : function(x, y){
37548         this.fireEvent("beforeresize", this);
37549         if(!this.overlay){
37550             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37551             o.unselectable();
37552             o.enableDisplayMode("block");
37553             // all splitbars share the same overlay
37554             Roo.bootstrap.SplitBar.prototype.overlay = o;
37555         }
37556         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37557         this.overlay.show();
37558         Roo.get(this.proxy).setDisplayed("block");
37559         var size = this.adapter.getElementSize(this);
37560         this.activeMinSize = this.getMinimumSize();;
37561         this.activeMaxSize = this.getMaximumSize();;
37562         var c1 = size - this.activeMinSize;
37563         var c2 = Math.max(this.activeMaxSize - size, 0);
37564         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37565             this.dd.resetConstraints();
37566             this.dd.setXConstraint(
37567                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37568                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37569             );
37570             this.dd.setYConstraint(0, 0);
37571         }else{
37572             this.dd.resetConstraints();
37573             this.dd.setXConstraint(0, 0);
37574             this.dd.setYConstraint(
37575                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37576                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37577             );
37578          }
37579         this.dragSpecs.startSize = size;
37580         this.dragSpecs.startPoint = [x, y];
37581         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37582     },
37583     
37584     /** 
37585      * @private Called after the drag operation by the DDProxy
37586      */
37587     onEndProxyDrag : function(e){
37588         Roo.get(this.proxy).setDisplayed(false);
37589         var endPoint = Roo.lib.Event.getXY(e);
37590         if(this.overlay){
37591             this.overlay.hide();
37592         }
37593         var newSize;
37594         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37595             newSize = this.dragSpecs.startSize + 
37596                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37597                     endPoint[0] - this.dragSpecs.startPoint[0] :
37598                     this.dragSpecs.startPoint[0] - endPoint[0]
37599                 );
37600         }else{
37601             newSize = this.dragSpecs.startSize + 
37602                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37603                     endPoint[1] - this.dragSpecs.startPoint[1] :
37604                     this.dragSpecs.startPoint[1] - endPoint[1]
37605                 );
37606         }
37607         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37608         if(newSize != this.dragSpecs.startSize){
37609             if(this.fireEvent('beforeapply', this, newSize) !== false){
37610                 this.adapter.setElementSize(this, newSize);
37611                 this.fireEvent("moved", this, newSize);
37612                 this.fireEvent("resize", this, newSize);
37613             }
37614         }
37615     },
37616     
37617     /**
37618      * Get the adapter this SplitBar uses
37619      * @return The adapter object
37620      */
37621     getAdapter : function(){
37622         return this.adapter;
37623     },
37624     
37625     /**
37626      * Set the adapter this SplitBar uses
37627      * @param {Object} adapter A SplitBar adapter object
37628      */
37629     setAdapter : function(adapter){
37630         this.adapter = adapter;
37631         this.adapter.init(this);
37632     },
37633     
37634     /**
37635      * Gets the minimum size for the resizing element
37636      * @return {Number} The minimum size
37637      */
37638     getMinimumSize : function(){
37639         return this.minSize;
37640     },
37641     
37642     /**
37643      * Sets the minimum size for the resizing element
37644      * @param {Number} minSize The minimum size
37645      */
37646     setMinimumSize : function(minSize){
37647         this.minSize = minSize;
37648     },
37649     
37650     /**
37651      * Gets the maximum size for the resizing element
37652      * @return {Number} The maximum size
37653      */
37654     getMaximumSize : function(){
37655         return this.maxSize;
37656     },
37657     
37658     /**
37659      * Sets the maximum size for the resizing element
37660      * @param {Number} maxSize The maximum size
37661      */
37662     setMaximumSize : function(maxSize){
37663         this.maxSize = maxSize;
37664     },
37665     
37666     /**
37667      * Sets the initialize size for the resizing element
37668      * @param {Number} size The initial size
37669      */
37670     setCurrentSize : function(size){
37671         var oldAnimate = this.animate;
37672         this.animate = false;
37673         this.adapter.setElementSize(this, size);
37674         this.animate = oldAnimate;
37675     },
37676     
37677     /**
37678      * Destroy this splitbar. 
37679      * @param {Boolean} removeEl True to remove the element
37680      */
37681     destroy : function(removeEl){
37682         if(this.shim){
37683             this.shim.remove();
37684         }
37685         this.dd.unreg();
37686         this.proxy.parentNode.removeChild(this.proxy);
37687         if(removeEl){
37688             this.el.remove();
37689         }
37690     }
37691 });
37692
37693 /**
37694  * @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.
37695  */
37696 Roo.bootstrap.SplitBar.createProxy = function(dir){
37697     var proxy = new Roo.Element(document.createElement("div"));
37698     proxy.unselectable();
37699     var cls = 'roo-splitbar-proxy';
37700     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37701     document.body.appendChild(proxy.dom);
37702     return proxy.dom;
37703 };
37704
37705 /** 
37706  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37707  * Default Adapter. It assumes the splitter and resizing element are not positioned
37708  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37709  */
37710 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37711 };
37712
37713 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37714     // do nothing for now
37715     init : function(s){
37716     
37717     },
37718     /**
37719      * Called before drag operations to get the current size of the resizing element. 
37720      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37721      */
37722      getElementSize : function(s){
37723         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37724             return s.resizingEl.getWidth();
37725         }else{
37726             return s.resizingEl.getHeight();
37727         }
37728     },
37729     
37730     /**
37731      * Called after drag operations to set the size of the resizing element.
37732      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37733      * @param {Number} newSize The new size to set
37734      * @param {Function} onComplete A function to be invoked when resizing is complete
37735      */
37736     setElementSize : function(s, newSize, onComplete){
37737         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37738             if(!s.animate){
37739                 s.resizingEl.setWidth(newSize);
37740                 if(onComplete){
37741                     onComplete(s, newSize);
37742                 }
37743             }else{
37744                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37745             }
37746         }else{
37747             
37748             if(!s.animate){
37749                 s.resizingEl.setHeight(newSize);
37750                 if(onComplete){
37751                     onComplete(s, newSize);
37752                 }
37753             }else{
37754                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37755             }
37756         }
37757     }
37758 };
37759
37760 /** 
37761  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37762  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37763  * Adapter that  moves the splitter element to align with the resized sizing element. 
37764  * Used with an absolute positioned SplitBar.
37765  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37766  * document.body, make sure you assign an id to the body element.
37767  */
37768 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37769     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37770     this.container = Roo.get(container);
37771 };
37772
37773 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37774     init : function(s){
37775         this.basic.init(s);
37776     },
37777     
37778     getElementSize : function(s){
37779         return this.basic.getElementSize(s);
37780     },
37781     
37782     setElementSize : function(s, newSize, onComplete){
37783         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37784     },
37785     
37786     moveSplitter : function(s){
37787         var yes = Roo.bootstrap.SplitBar;
37788         switch(s.placement){
37789             case yes.LEFT:
37790                 s.el.setX(s.resizingEl.getRight());
37791                 break;
37792             case yes.RIGHT:
37793                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37794                 break;
37795             case yes.TOP:
37796                 s.el.setY(s.resizingEl.getBottom());
37797                 break;
37798             case yes.BOTTOM:
37799                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37800                 break;
37801         }
37802     }
37803 };
37804
37805 /**
37806  * Orientation constant - Create a vertical SplitBar
37807  * @static
37808  * @type Number
37809  */
37810 Roo.bootstrap.SplitBar.VERTICAL = 1;
37811
37812 /**
37813  * Orientation constant - Create a horizontal SplitBar
37814  * @static
37815  * @type Number
37816  */
37817 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37818
37819 /**
37820  * Placement constant - The resizing element is to the left of the splitter element
37821  * @static
37822  * @type Number
37823  */
37824 Roo.bootstrap.SplitBar.LEFT = 1;
37825
37826 /**
37827  * Placement constant - The resizing element is to the right of the splitter element
37828  * @static
37829  * @type Number
37830  */
37831 Roo.bootstrap.SplitBar.RIGHT = 2;
37832
37833 /**
37834  * Placement constant - The resizing element is positioned above the splitter element
37835  * @static
37836  * @type Number
37837  */
37838 Roo.bootstrap.SplitBar.TOP = 3;
37839
37840 /**
37841  * Placement constant - The resizing element is positioned under splitter element
37842  * @static
37843  * @type Number
37844  */
37845 Roo.bootstrap.SplitBar.BOTTOM = 4;
37846 /*
37847  * Based on:
37848  * Ext JS Library 1.1.1
37849  * Copyright(c) 2006-2007, Ext JS, LLC.
37850  *
37851  * Originally Released Under LGPL - original licence link has changed is not relivant.
37852  *
37853  * Fork - LGPL
37854  * <script type="text/javascript">
37855  */
37856
37857 /**
37858  * @class Roo.bootstrap.layout.Manager
37859  * @extends Roo.bootstrap.Component
37860  * @abstract
37861  * Base class for layout managers.
37862  */
37863 Roo.bootstrap.layout.Manager = function(config)
37864 {
37865     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37866
37867
37868
37869
37870
37871     /** false to disable window resize monitoring @type Boolean */
37872     this.monitorWindowResize = true;
37873     this.regions = {};
37874     this.addEvents({
37875         /**
37876          * @event layout
37877          * Fires when a layout is performed.
37878          * @param {Roo.LayoutManager} this
37879          */
37880         "layout" : true,
37881         /**
37882          * @event regionresized
37883          * Fires when the user resizes a region.
37884          * @param {Roo.LayoutRegion} region The resized region
37885          * @param {Number} newSize The new size (width for east/west, height for north/south)
37886          */
37887         "regionresized" : true,
37888         /**
37889          * @event regioncollapsed
37890          * Fires when a region is collapsed.
37891          * @param {Roo.LayoutRegion} region The collapsed region
37892          */
37893         "regioncollapsed" : true,
37894         /**
37895          * @event regionexpanded
37896          * Fires when a region is expanded.
37897          * @param {Roo.LayoutRegion} region The expanded region
37898          */
37899         "regionexpanded" : true
37900     });
37901     this.updating = false;
37902
37903     if (config.el) {
37904         this.el = Roo.get(config.el);
37905         this.initEvents();
37906     }
37907
37908 };
37909
37910 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37911
37912
37913     regions : null,
37914
37915     monitorWindowResize : true,
37916
37917
37918     updating : false,
37919
37920
37921     onRender : function(ct, position)
37922     {
37923         if(!this.el){
37924             this.el = Roo.get(ct);
37925             this.initEvents();
37926         }
37927         //this.fireEvent('render',this);
37928     },
37929
37930
37931     initEvents: function()
37932     {
37933
37934
37935         // ie scrollbar fix
37936         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37937             document.body.scroll = "no";
37938         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37939             this.el.position('relative');
37940         }
37941         this.id = this.el.id;
37942         this.el.addClass("roo-layout-container");
37943         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37944         if(this.el.dom != document.body ) {
37945             this.el.on('resize', this.layout,this);
37946             this.el.on('show', this.layout,this);
37947         }
37948
37949     },
37950
37951     /**
37952      * Returns true if this layout is currently being updated
37953      * @return {Boolean}
37954      */
37955     isUpdating : function(){
37956         return this.updating;
37957     },
37958
37959     /**
37960      * Suspend the LayoutManager from doing auto-layouts while
37961      * making multiple add or remove calls
37962      */
37963     beginUpdate : function(){
37964         this.updating = true;
37965     },
37966
37967     /**
37968      * Restore auto-layouts and optionally disable the manager from performing a layout
37969      * @param {Boolean} noLayout true to disable a layout update
37970      */
37971     endUpdate : function(noLayout){
37972         this.updating = false;
37973         if(!noLayout){
37974             this.layout();
37975         }
37976     },
37977
37978     layout: function(){
37979         // abstract...
37980     },
37981
37982     onRegionResized : function(region, newSize){
37983         this.fireEvent("regionresized", region, newSize);
37984         this.layout();
37985     },
37986
37987     onRegionCollapsed : function(region){
37988         this.fireEvent("regioncollapsed", region);
37989     },
37990
37991     onRegionExpanded : function(region){
37992         this.fireEvent("regionexpanded", region);
37993     },
37994
37995     /**
37996      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37997      * performs box-model adjustments.
37998      * @return {Object} The size as an object {width: (the width), height: (the height)}
37999      */
38000     getViewSize : function()
38001     {
38002         var size;
38003         if(this.el.dom != document.body){
38004             size = this.el.getSize();
38005         }else{
38006             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38007         }
38008         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38009         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38010         return size;
38011     },
38012
38013     /**
38014      * Returns the Element this layout is bound to.
38015      * @return {Roo.Element}
38016      */
38017     getEl : function(){
38018         return this.el;
38019     },
38020
38021     /**
38022      * Returns the specified region.
38023      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38024      * @return {Roo.LayoutRegion}
38025      */
38026     getRegion : function(target){
38027         return this.regions[target.toLowerCase()];
38028     },
38029
38030     onWindowResize : function(){
38031         if(this.monitorWindowResize){
38032             this.layout();
38033         }
38034     }
38035 });
38036 /*
38037  * Based on:
38038  * Ext JS Library 1.1.1
38039  * Copyright(c) 2006-2007, Ext JS, LLC.
38040  *
38041  * Originally Released Under LGPL - original licence link has changed is not relivant.
38042  *
38043  * Fork - LGPL
38044  * <script type="text/javascript">
38045  */
38046 /**
38047  * @class Roo.bootstrap.layout.Border
38048  * @extends Roo.bootstrap.layout.Manager
38049  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38050  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38051  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38052  * please see: examples/bootstrap/nested.html<br><br>
38053  
38054 <b>The container the layout is rendered into can be either the body element or any other element.
38055 If it is not the body element, the container needs to either be an absolute positioned element,
38056 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38057 the container size if it is not the body element.</b>
38058
38059 * @constructor
38060 * Create a new Border
38061 * @param {Object} config Configuration options
38062  */
38063 Roo.bootstrap.layout.Border = function(config){
38064     config = config || {};
38065     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38066     
38067     
38068     
38069     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38070         if(config[region]){
38071             config[region].region = region;
38072             this.addRegion(config[region]);
38073         }
38074     },this);
38075     
38076 };
38077
38078 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38079
38080 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38081     
38082         /**
38083          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38084          */
38085         /**
38086          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38087          */
38088         /**
38089          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38090          */
38091         /**
38092          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38093          */
38094         /**
38095          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38096          */
38097         
38098         
38099         
38100         
38101     parent : false, // this might point to a 'nest' or a ???
38102     
38103     /**
38104      * Creates and adds a new region if it doesn't already exist.
38105      * @param {String} target The target region key (north, south, east, west or center).
38106      * @param {Object} config The regions config object
38107      * @return {BorderLayoutRegion} The new region
38108      */
38109     addRegion : function(config)
38110     {
38111         if(!this.regions[config.region]){
38112             var r = this.factory(config);
38113             this.bindRegion(r);
38114         }
38115         return this.regions[config.region];
38116     },
38117
38118     // private (kinda)
38119     bindRegion : function(r){
38120         this.regions[r.config.region] = r;
38121         
38122         r.on("visibilitychange",    this.layout, this);
38123         r.on("paneladded",          this.layout, this);
38124         r.on("panelremoved",        this.layout, this);
38125         r.on("invalidated",         this.layout, this);
38126         r.on("resized",             this.onRegionResized, this);
38127         r.on("collapsed",           this.onRegionCollapsed, this);
38128         r.on("expanded",            this.onRegionExpanded, this);
38129     },
38130
38131     /**
38132      * Performs a layout update.
38133      */
38134     layout : function()
38135     {
38136         if(this.updating) {
38137             return;
38138         }
38139         
38140         // render all the rebions if they have not been done alreayd?
38141         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38142             if(this.regions[region] && !this.regions[region].bodyEl){
38143                 this.regions[region].onRender(this.el)
38144             }
38145         },this);
38146         
38147         var size = this.getViewSize();
38148         var w = size.width;
38149         var h = size.height;
38150         var centerW = w;
38151         var centerH = h;
38152         var centerY = 0;
38153         var centerX = 0;
38154         //var x = 0, y = 0;
38155
38156         var rs = this.regions;
38157         var north = rs["north"];
38158         var south = rs["south"]; 
38159         var west = rs["west"];
38160         var east = rs["east"];
38161         var center = rs["center"];
38162         //if(this.hideOnLayout){ // not supported anymore
38163             //c.el.setStyle("display", "none");
38164         //}
38165         if(north && north.isVisible()){
38166             var b = north.getBox();
38167             var m = north.getMargins();
38168             b.width = w - (m.left+m.right);
38169             b.x = m.left;
38170             b.y = m.top;
38171             centerY = b.height + b.y + m.bottom;
38172             centerH -= centerY;
38173             north.updateBox(this.safeBox(b));
38174         }
38175         if(south && south.isVisible()){
38176             var b = south.getBox();
38177             var m = south.getMargins();
38178             b.width = w - (m.left+m.right);
38179             b.x = m.left;
38180             var totalHeight = (b.height + m.top + m.bottom);
38181             b.y = h - totalHeight + m.top;
38182             centerH -= totalHeight;
38183             south.updateBox(this.safeBox(b));
38184         }
38185         if(west && west.isVisible()){
38186             var b = west.getBox();
38187             var m = west.getMargins();
38188             b.height = centerH - (m.top+m.bottom);
38189             b.x = m.left;
38190             b.y = centerY + m.top;
38191             var totalWidth = (b.width + m.left + m.right);
38192             centerX += totalWidth;
38193             centerW -= totalWidth;
38194             west.updateBox(this.safeBox(b));
38195         }
38196         if(east && east.isVisible()){
38197             var b = east.getBox();
38198             var m = east.getMargins();
38199             b.height = centerH - (m.top+m.bottom);
38200             var totalWidth = (b.width + m.left + m.right);
38201             b.x = w - totalWidth + m.left;
38202             b.y = centerY + m.top;
38203             centerW -= totalWidth;
38204             east.updateBox(this.safeBox(b));
38205         }
38206         if(center){
38207             var m = center.getMargins();
38208             var centerBox = {
38209                 x: centerX + m.left,
38210                 y: centerY + m.top,
38211                 width: centerW - (m.left+m.right),
38212                 height: centerH - (m.top+m.bottom)
38213             };
38214             //if(this.hideOnLayout){
38215                 //center.el.setStyle("display", "block");
38216             //}
38217             center.updateBox(this.safeBox(centerBox));
38218         }
38219         this.el.repaint();
38220         this.fireEvent("layout", this);
38221     },
38222
38223     // private
38224     safeBox : function(box){
38225         box.width = Math.max(0, box.width);
38226         box.height = Math.max(0, box.height);
38227         return box;
38228     },
38229
38230     /**
38231      * Adds a ContentPanel (or subclass) to this layout.
38232      * @param {String} target The target region key (north, south, east, west or center).
38233      * @param {Roo.ContentPanel} panel The panel to add
38234      * @return {Roo.ContentPanel} The added panel
38235      */
38236     add : function(target, panel){
38237          
38238         target = target.toLowerCase();
38239         return this.regions[target].add(panel);
38240     },
38241
38242     /**
38243      * Remove a ContentPanel (or subclass) to this layout.
38244      * @param {String} target The target region key (north, south, east, west or center).
38245      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38246      * @return {Roo.ContentPanel} The removed panel
38247      */
38248     remove : function(target, panel){
38249         target = target.toLowerCase();
38250         return this.regions[target].remove(panel);
38251     },
38252
38253     /**
38254      * Searches all regions for a panel with the specified id
38255      * @param {String} panelId
38256      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38257      */
38258     findPanel : function(panelId){
38259         var rs = this.regions;
38260         for(var target in rs){
38261             if(typeof rs[target] != "function"){
38262                 var p = rs[target].getPanel(panelId);
38263                 if(p){
38264                     return p;
38265                 }
38266             }
38267         }
38268         return null;
38269     },
38270
38271     /**
38272      * Searches all regions for a panel with the specified id and activates (shows) it.
38273      * @param {String/ContentPanel} panelId The panels id or the panel itself
38274      * @return {Roo.ContentPanel} The shown panel or null
38275      */
38276     showPanel : function(panelId) {
38277       var rs = this.regions;
38278       for(var target in rs){
38279          var r = rs[target];
38280          if(typeof r != "function"){
38281             if(r.hasPanel(panelId)){
38282                return r.showPanel(panelId);
38283             }
38284          }
38285       }
38286       return null;
38287    },
38288
38289    /**
38290      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38291      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38292      */
38293    /*
38294     restoreState : function(provider){
38295         if(!provider){
38296             provider = Roo.state.Manager;
38297         }
38298         var sm = new Roo.LayoutStateManager();
38299         sm.init(this, provider);
38300     },
38301 */
38302  
38303  
38304     /**
38305      * Adds a xtype elements to the layout.
38306      * <pre><code>
38307
38308 layout.addxtype({
38309        xtype : 'ContentPanel',
38310        region: 'west',
38311        items: [ .... ]
38312    }
38313 );
38314
38315 layout.addxtype({
38316         xtype : 'NestedLayoutPanel',
38317         region: 'west',
38318         layout: {
38319            center: { },
38320            west: { }   
38321         },
38322         items : [ ... list of content panels or nested layout panels.. ]
38323    }
38324 );
38325 </code></pre>
38326      * @param {Object} cfg Xtype definition of item to add.
38327      */
38328     addxtype : function(cfg)
38329     {
38330         // basically accepts a pannel...
38331         // can accept a layout region..!?!?
38332         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38333         
38334         
38335         // theory?  children can only be panels??
38336         
38337         //if (!cfg.xtype.match(/Panel$/)) {
38338         //    return false;
38339         //}
38340         var ret = false;
38341         
38342         if (typeof(cfg.region) == 'undefined') {
38343             Roo.log("Failed to add Panel, region was not set");
38344             Roo.log(cfg);
38345             return false;
38346         }
38347         var region = cfg.region;
38348         delete cfg.region;
38349         
38350           
38351         var xitems = [];
38352         if (cfg.items) {
38353             xitems = cfg.items;
38354             delete cfg.items;
38355         }
38356         var nb = false;
38357         
38358         if ( region == 'center') {
38359             Roo.log("Center: " + cfg.title);
38360         }
38361         
38362         
38363         switch(cfg.xtype) 
38364         {
38365             case 'Content':  // ContentPanel (el, cfg)
38366             case 'Scroll':  // ContentPanel (el, cfg)
38367             case 'View': 
38368                 cfg.autoCreate = cfg.autoCreate || true;
38369                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38370                 //} else {
38371                 //    var el = this.el.createChild();
38372                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38373                 //}
38374                 
38375                 this.add(region, ret);
38376                 break;
38377             
38378             /*
38379             case 'TreePanel': // our new panel!
38380                 cfg.el = this.el.createChild();
38381                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38382                 this.add(region, ret);
38383                 break;
38384             */
38385             
38386             case 'Nest': 
38387                 // create a new Layout (which is  a Border Layout...
38388                 
38389                 var clayout = cfg.layout;
38390                 clayout.el  = this.el.createChild();
38391                 clayout.items   = clayout.items  || [];
38392                 
38393                 delete cfg.layout;
38394                 
38395                 // replace this exitems with the clayout ones..
38396                 xitems = clayout.items;
38397                  
38398                 // force background off if it's in center...
38399                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38400                     cfg.background = false;
38401                 }
38402                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38403                 
38404                 
38405                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38406                 //console.log('adding nested layout panel '  + cfg.toSource());
38407                 this.add(region, ret);
38408                 nb = {}; /// find first...
38409                 break;
38410             
38411             case 'Grid':
38412                 
38413                 // needs grid and region
38414                 
38415                 //var el = this.getRegion(region).el.createChild();
38416                 /*
38417                  *var el = this.el.createChild();
38418                 // create the grid first...
38419                 cfg.grid.container = el;
38420                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38421                 */
38422                 
38423                 if (region == 'center' && this.active ) {
38424                     cfg.background = false;
38425                 }
38426                 
38427                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38428                 
38429                 this.add(region, ret);
38430                 /*
38431                 if (cfg.background) {
38432                     // render grid on panel activation (if panel background)
38433                     ret.on('activate', function(gp) {
38434                         if (!gp.grid.rendered) {
38435                     //        gp.grid.render(el);
38436                         }
38437                     });
38438                 } else {
38439                   //  cfg.grid.render(el);
38440                 }
38441                 */
38442                 break;
38443            
38444            
38445             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38446                 // it was the old xcomponent building that caused this before.
38447                 // espeically if border is the top element in the tree.
38448                 ret = this;
38449                 break; 
38450                 
38451                     
38452                 
38453                 
38454                 
38455             default:
38456                 /*
38457                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38458                     
38459                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38460                     this.add(region, ret);
38461                 } else {
38462                 */
38463                     Roo.log(cfg);
38464                     throw "Can not add '" + cfg.xtype + "' to Border";
38465                     return null;
38466              
38467                                 
38468              
38469         }
38470         this.beginUpdate();
38471         // add children..
38472         var region = '';
38473         var abn = {};
38474         Roo.each(xitems, function(i)  {
38475             region = nb && i.region ? i.region : false;
38476             
38477             var add = ret.addxtype(i);
38478            
38479             if (region) {
38480                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38481                 if (!i.background) {
38482                     abn[region] = nb[region] ;
38483                 }
38484             }
38485             
38486         });
38487         this.endUpdate();
38488
38489         // make the last non-background panel active..
38490         //if (nb) { Roo.log(abn); }
38491         if (nb) {
38492             
38493             for(var r in abn) {
38494                 region = this.getRegion(r);
38495                 if (region) {
38496                     // tried using nb[r], but it does not work..
38497                      
38498                     region.showPanel(abn[r]);
38499                    
38500                 }
38501             }
38502         }
38503         return ret;
38504         
38505     },
38506     
38507     
38508 // private
38509     factory : function(cfg)
38510     {
38511         
38512         var validRegions = Roo.bootstrap.layout.Border.regions;
38513
38514         var target = cfg.region;
38515         cfg.mgr = this;
38516         
38517         var r = Roo.bootstrap.layout;
38518         Roo.log(target);
38519         switch(target){
38520             case "north":
38521                 return new r.North(cfg);
38522             case "south":
38523                 return new r.South(cfg);
38524             case "east":
38525                 return new r.East(cfg);
38526             case "west":
38527                 return new r.West(cfg);
38528             case "center":
38529                 return new r.Center(cfg);
38530         }
38531         throw 'Layout region "'+target+'" not supported.';
38532     }
38533     
38534     
38535 });
38536  /*
38537  * Based on:
38538  * Ext JS Library 1.1.1
38539  * Copyright(c) 2006-2007, Ext JS, LLC.
38540  *
38541  * Originally Released Under LGPL - original licence link has changed is not relivant.
38542  *
38543  * Fork - LGPL
38544  * <script type="text/javascript">
38545  */
38546  
38547 /**
38548  * @class Roo.bootstrap.layout.Basic
38549  * @extends Roo.util.Observable
38550  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38551  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38552  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38553  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38554  * @cfg {string}   region  the region that it inhabits..
38555  * @cfg {bool}   skipConfig skip config?
38556  * 
38557
38558  */
38559 Roo.bootstrap.layout.Basic = function(config){
38560     
38561     this.mgr = config.mgr;
38562     
38563     this.position = config.region;
38564     
38565     var skipConfig = config.skipConfig;
38566     
38567     this.events = {
38568         /**
38569          * @scope Roo.BasicLayoutRegion
38570          */
38571         
38572         /**
38573          * @event beforeremove
38574          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38575          * @param {Roo.LayoutRegion} this
38576          * @param {Roo.ContentPanel} panel The panel
38577          * @param {Object} e The cancel event object
38578          */
38579         "beforeremove" : true,
38580         /**
38581          * @event invalidated
38582          * Fires when the layout for this region is changed.
38583          * @param {Roo.LayoutRegion} this
38584          */
38585         "invalidated" : true,
38586         /**
38587          * @event visibilitychange
38588          * Fires when this region is shown or hidden 
38589          * @param {Roo.LayoutRegion} this
38590          * @param {Boolean} visibility true or false
38591          */
38592         "visibilitychange" : true,
38593         /**
38594          * @event paneladded
38595          * Fires when a panel is added. 
38596          * @param {Roo.LayoutRegion} this
38597          * @param {Roo.ContentPanel} panel The panel
38598          */
38599         "paneladded" : true,
38600         /**
38601          * @event panelremoved
38602          * Fires when a panel is removed. 
38603          * @param {Roo.LayoutRegion} this
38604          * @param {Roo.ContentPanel} panel The panel
38605          */
38606         "panelremoved" : true,
38607         /**
38608          * @event beforecollapse
38609          * Fires when this region before collapse.
38610          * @param {Roo.LayoutRegion} this
38611          */
38612         "beforecollapse" : true,
38613         /**
38614          * @event collapsed
38615          * Fires when this region is collapsed.
38616          * @param {Roo.LayoutRegion} this
38617          */
38618         "collapsed" : true,
38619         /**
38620          * @event expanded
38621          * Fires when this region is expanded.
38622          * @param {Roo.LayoutRegion} this
38623          */
38624         "expanded" : true,
38625         /**
38626          * @event slideshow
38627          * Fires when this region is slid into view.
38628          * @param {Roo.LayoutRegion} this
38629          */
38630         "slideshow" : true,
38631         /**
38632          * @event slidehide
38633          * Fires when this region slides out of view. 
38634          * @param {Roo.LayoutRegion} this
38635          */
38636         "slidehide" : true,
38637         /**
38638          * @event panelactivated
38639          * Fires when a panel is activated. 
38640          * @param {Roo.LayoutRegion} this
38641          * @param {Roo.ContentPanel} panel The activated panel
38642          */
38643         "panelactivated" : true,
38644         /**
38645          * @event resized
38646          * Fires when the user resizes this region. 
38647          * @param {Roo.LayoutRegion} this
38648          * @param {Number} newSize The new size (width for east/west, height for north/south)
38649          */
38650         "resized" : true
38651     };
38652     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38653     this.panels = new Roo.util.MixedCollection();
38654     this.panels.getKey = this.getPanelId.createDelegate(this);
38655     this.box = null;
38656     this.activePanel = null;
38657     // ensure listeners are added...
38658     
38659     if (config.listeners || config.events) {
38660         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38661             listeners : config.listeners || {},
38662             events : config.events || {}
38663         });
38664     }
38665     
38666     if(skipConfig !== true){
38667         this.applyConfig(config);
38668     }
38669 };
38670
38671 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38672 {
38673     getPanelId : function(p){
38674         return p.getId();
38675     },
38676     
38677     applyConfig : function(config){
38678         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38679         this.config = config;
38680         
38681     },
38682     
38683     /**
38684      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38685      * the width, for horizontal (north, south) the height.
38686      * @param {Number} newSize The new width or height
38687      */
38688     resizeTo : function(newSize){
38689         var el = this.el ? this.el :
38690                  (this.activePanel ? this.activePanel.getEl() : null);
38691         if(el){
38692             switch(this.position){
38693                 case "east":
38694                 case "west":
38695                     el.setWidth(newSize);
38696                     this.fireEvent("resized", this, newSize);
38697                 break;
38698                 case "north":
38699                 case "south":
38700                     el.setHeight(newSize);
38701                     this.fireEvent("resized", this, newSize);
38702                 break;                
38703             }
38704         }
38705     },
38706     
38707     getBox : function(){
38708         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38709     },
38710     
38711     getMargins : function(){
38712         return this.margins;
38713     },
38714     
38715     updateBox : function(box){
38716         this.box = box;
38717         var el = this.activePanel.getEl();
38718         el.dom.style.left = box.x + "px";
38719         el.dom.style.top = box.y + "px";
38720         this.activePanel.setSize(box.width, box.height);
38721     },
38722     
38723     /**
38724      * Returns the container element for this region.
38725      * @return {Roo.Element}
38726      */
38727     getEl : function(){
38728         return this.activePanel;
38729     },
38730     
38731     /**
38732      * Returns true if this region is currently visible.
38733      * @return {Boolean}
38734      */
38735     isVisible : function(){
38736         return this.activePanel ? true : false;
38737     },
38738     
38739     setActivePanel : function(panel){
38740         panel = this.getPanel(panel);
38741         if(this.activePanel && this.activePanel != panel){
38742             this.activePanel.setActiveState(false);
38743             this.activePanel.getEl().setLeftTop(-10000,-10000);
38744         }
38745         this.activePanel = panel;
38746         panel.setActiveState(true);
38747         if(this.box){
38748             panel.setSize(this.box.width, this.box.height);
38749         }
38750         this.fireEvent("panelactivated", this, panel);
38751         this.fireEvent("invalidated");
38752     },
38753     
38754     /**
38755      * Show the specified panel.
38756      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38757      * @return {Roo.ContentPanel} The shown panel or null
38758      */
38759     showPanel : function(panel){
38760         panel = this.getPanel(panel);
38761         if(panel){
38762             this.setActivePanel(panel);
38763         }
38764         return panel;
38765     },
38766     
38767     /**
38768      * Get the active panel for this region.
38769      * @return {Roo.ContentPanel} The active panel or null
38770      */
38771     getActivePanel : function(){
38772         return this.activePanel;
38773     },
38774     
38775     /**
38776      * Add the passed ContentPanel(s)
38777      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38778      * @return {Roo.ContentPanel} The panel added (if only one was added)
38779      */
38780     add : function(panel){
38781         if(arguments.length > 1){
38782             for(var i = 0, len = arguments.length; i < len; i++) {
38783                 this.add(arguments[i]);
38784             }
38785             return null;
38786         }
38787         if(this.hasPanel(panel)){
38788             this.showPanel(panel);
38789             return panel;
38790         }
38791         var el = panel.getEl();
38792         if(el.dom.parentNode != this.mgr.el.dom){
38793             this.mgr.el.dom.appendChild(el.dom);
38794         }
38795         if(panel.setRegion){
38796             panel.setRegion(this);
38797         }
38798         this.panels.add(panel);
38799         el.setStyle("position", "absolute");
38800         if(!panel.background){
38801             this.setActivePanel(panel);
38802             if(this.config.initialSize && this.panels.getCount()==1){
38803                 this.resizeTo(this.config.initialSize);
38804             }
38805         }
38806         this.fireEvent("paneladded", this, panel);
38807         return panel;
38808     },
38809     
38810     /**
38811      * Returns true if the panel is in this region.
38812      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38813      * @return {Boolean}
38814      */
38815     hasPanel : function(panel){
38816         if(typeof panel == "object"){ // must be panel obj
38817             panel = panel.getId();
38818         }
38819         return this.getPanel(panel) ? true : false;
38820     },
38821     
38822     /**
38823      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38824      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38825      * @param {Boolean} preservePanel Overrides the config preservePanel option
38826      * @return {Roo.ContentPanel} The panel that was removed
38827      */
38828     remove : function(panel, preservePanel){
38829         panel = this.getPanel(panel);
38830         if(!panel){
38831             return null;
38832         }
38833         var e = {};
38834         this.fireEvent("beforeremove", this, panel, e);
38835         if(e.cancel === true){
38836             return null;
38837         }
38838         var panelId = panel.getId();
38839         this.panels.removeKey(panelId);
38840         return panel;
38841     },
38842     
38843     /**
38844      * Returns the panel specified or null if it's not in this region.
38845      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38846      * @return {Roo.ContentPanel}
38847      */
38848     getPanel : function(id){
38849         if(typeof id == "object"){ // must be panel obj
38850             return id;
38851         }
38852         return this.panels.get(id);
38853     },
38854     
38855     /**
38856      * Returns this regions position (north/south/east/west/center).
38857      * @return {String} 
38858      */
38859     getPosition: function(){
38860         return this.position;    
38861     }
38862 });/*
38863  * Based on:
38864  * Ext JS Library 1.1.1
38865  * Copyright(c) 2006-2007, Ext JS, LLC.
38866  *
38867  * Originally Released Under LGPL - original licence link has changed is not relivant.
38868  *
38869  * Fork - LGPL
38870  * <script type="text/javascript">
38871  */
38872  
38873 /**
38874  * @class Roo.bootstrap.layout.Region
38875  * @extends Roo.bootstrap.layout.Basic
38876  * This class represents a region in a layout manager.
38877  
38878  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38879  * @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})
38880  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38881  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38882  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38883  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38884  * @cfg {String}    title           The title for the region (overrides panel titles)
38885  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38886  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38887  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38888  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38889  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38890  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38891  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38892  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38893  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38894  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38895
38896  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38897  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38898  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38899  * @cfg {Number}    width           For East/West panels
38900  * @cfg {Number}    height          For North/South panels
38901  * @cfg {Boolean}   split           To show the splitter
38902  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38903  * 
38904  * @cfg {string}   cls             Extra CSS classes to add to region
38905  * 
38906  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38907  * @cfg {string}   region  the region that it inhabits..
38908  *
38909
38910  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38911  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38912
38913  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38914  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38915  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38916  */
38917 Roo.bootstrap.layout.Region = function(config)
38918 {
38919     this.applyConfig(config);
38920
38921     var mgr = config.mgr;
38922     var pos = config.region;
38923     config.skipConfig = true;
38924     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38925     
38926     if (mgr.el) {
38927         this.onRender(mgr.el);   
38928     }
38929      
38930     this.visible = true;
38931     this.collapsed = false;
38932     this.unrendered_panels = [];
38933 };
38934
38935 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38936
38937     position: '', // set by wrapper (eg. north/south etc..)
38938     unrendered_panels : null,  // unrendered panels.
38939     
38940     tabPosition : false,
38941     
38942     mgr: false, // points to 'Border'
38943     
38944     
38945     createBody : function(){
38946         /** This region's body element 
38947         * @type Roo.Element */
38948         this.bodyEl = this.el.createChild({
38949                 tag: "div",
38950                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38951         });
38952     },
38953
38954     onRender: function(ctr, pos)
38955     {
38956         var dh = Roo.DomHelper;
38957         /** This region's container element 
38958         * @type Roo.Element */
38959         this.el = dh.append(ctr.dom, {
38960                 tag: "div",
38961                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38962             }, true);
38963         /** This region's title element 
38964         * @type Roo.Element */
38965     
38966         this.titleEl = dh.append(this.el.dom,  {
38967                 tag: "div",
38968                 unselectable: "on",
38969                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38970                 children:[
38971                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38972                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38973                 ]
38974             }, true);
38975         
38976         this.titleEl.enableDisplayMode();
38977         /** This region's title text element 
38978         * @type HTMLElement */
38979         this.titleTextEl = this.titleEl.dom.firstChild;
38980         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38981         /*
38982         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38983         this.closeBtn.enableDisplayMode();
38984         this.closeBtn.on("click", this.closeClicked, this);
38985         this.closeBtn.hide();
38986     */
38987         this.createBody(this.config);
38988         if(this.config.hideWhenEmpty){
38989             this.hide();
38990             this.on("paneladded", this.validateVisibility, this);
38991             this.on("panelremoved", this.validateVisibility, this);
38992         }
38993         if(this.autoScroll){
38994             this.bodyEl.setStyle("overflow", "auto");
38995         }else{
38996             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38997         }
38998         //if(c.titlebar !== false){
38999             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39000                 this.titleEl.hide();
39001             }else{
39002                 this.titleEl.show();
39003                 if(this.config.title){
39004                     this.titleTextEl.innerHTML = this.config.title;
39005                 }
39006             }
39007         //}
39008         if(this.config.collapsed){
39009             this.collapse(true);
39010         }
39011         if(this.config.hidden){
39012             this.hide();
39013         }
39014         
39015         if (this.unrendered_panels && this.unrendered_panels.length) {
39016             for (var i =0;i< this.unrendered_panels.length; i++) {
39017                 this.add(this.unrendered_panels[i]);
39018             }
39019             this.unrendered_panels = null;
39020             
39021         }
39022         
39023     },
39024     
39025     applyConfig : function(c)
39026     {
39027         /*
39028          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39029             var dh = Roo.DomHelper;
39030             if(c.titlebar !== false){
39031                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39032                 this.collapseBtn.on("click", this.collapse, this);
39033                 this.collapseBtn.enableDisplayMode();
39034                 /*
39035                 if(c.showPin === true || this.showPin){
39036                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39037                     this.stickBtn.enableDisplayMode();
39038                     this.stickBtn.on("click", this.expand, this);
39039                     this.stickBtn.hide();
39040                 }
39041                 
39042             }
39043             */
39044             /** This region's collapsed element
39045             * @type Roo.Element */
39046             /*
39047              *
39048             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39049                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39050             ]}, true);
39051             
39052             if(c.floatable !== false){
39053                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39054                this.collapsedEl.on("click", this.collapseClick, this);
39055             }
39056
39057             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39058                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39059                    id: "message", unselectable: "on", style:{"float":"left"}});
39060                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39061              }
39062             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39063             this.expandBtn.on("click", this.expand, this);
39064             
39065         }
39066         
39067         if(this.collapseBtn){
39068             this.collapseBtn.setVisible(c.collapsible == true);
39069         }
39070         
39071         this.cmargins = c.cmargins || this.cmargins ||
39072                          (this.position == "west" || this.position == "east" ?
39073                              {top: 0, left: 2, right:2, bottom: 0} :
39074                              {top: 2, left: 0, right:0, bottom: 2});
39075         */
39076         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39077         
39078         
39079         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39080         
39081         this.autoScroll = c.autoScroll || false;
39082         
39083         
39084        
39085         
39086         this.duration = c.duration || .30;
39087         this.slideDuration = c.slideDuration || .45;
39088         this.config = c;
39089        
39090     },
39091     /**
39092      * Returns true if this region is currently visible.
39093      * @return {Boolean}
39094      */
39095     isVisible : function(){
39096         return this.visible;
39097     },
39098
39099     /**
39100      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39101      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39102      */
39103     //setCollapsedTitle : function(title){
39104     //    title = title || "&#160;";
39105      //   if(this.collapsedTitleTextEl){
39106       //      this.collapsedTitleTextEl.innerHTML = title;
39107        // }
39108     //},
39109
39110     getBox : function(){
39111         var b;
39112       //  if(!this.collapsed){
39113             b = this.el.getBox(false, true);
39114        // }else{
39115           //  b = this.collapsedEl.getBox(false, true);
39116         //}
39117         return b;
39118     },
39119
39120     getMargins : function(){
39121         return this.margins;
39122         //return this.collapsed ? this.cmargins : this.margins;
39123     },
39124 /*
39125     highlight : function(){
39126         this.el.addClass("x-layout-panel-dragover");
39127     },
39128
39129     unhighlight : function(){
39130         this.el.removeClass("x-layout-panel-dragover");
39131     },
39132 */
39133     updateBox : function(box)
39134     {
39135         if (!this.bodyEl) {
39136             return; // not rendered yet..
39137         }
39138         
39139         this.box = box;
39140         if(!this.collapsed){
39141             this.el.dom.style.left = box.x + "px";
39142             this.el.dom.style.top = box.y + "px";
39143             this.updateBody(box.width, box.height);
39144         }else{
39145             this.collapsedEl.dom.style.left = box.x + "px";
39146             this.collapsedEl.dom.style.top = box.y + "px";
39147             this.collapsedEl.setSize(box.width, box.height);
39148         }
39149         if(this.tabs){
39150             this.tabs.autoSizeTabs();
39151         }
39152     },
39153
39154     updateBody : function(w, h)
39155     {
39156         if(w !== null){
39157             this.el.setWidth(w);
39158             w -= this.el.getBorderWidth("rl");
39159             if(this.config.adjustments){
39160                 w += this.config.adjustments[0];
39161             }
39162         }
39163         if(h !== null && h > 0){
39164             this.el.setHeight(h);
39165             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39166             h -= this.el.getBorderWidth("tb");
39167             if(this.config.adjustments){
39168                 h += this.config.adjustments[1];
39169             }
39170             this.bodyEl.setHeight(h);
39171             if(this.tabs){
39172                 h = this.tabs.syncHeight(h);
39173             }
39174         }
39175         if(this.panelSize){
39176             w = w !== null ? w : this.panelSize.width;
39177             h = h !== null ? h : this.panelSize.height;
39178         }
39179         if(this.activePanel){
39180             var el = this.activePanel.getEl();
39181             w = w !== null ? w : el.getWidth();
39182             h = h !== null ? h : el.getHeight();
39183             this.panelSize = {width: w, height: h};
39184             this.activePanel.setSize(w, h);
39185         }
39186         if(Roo.isIE && this.tabs){
39187             this.tabs.el.repaint();
39188         }
39189     },
39190
39191     /**
39192      * Returns the container element for this region.
39193      * @return {Roo.Element}
39194      */
39195     getEl : function(){
39196         return this.el;
39197     },
39198
39199     /**
39200      * Hides this region.
39201      */
39202     hide : function(){
39203         //if(!this.collapsed){
39204             this.el.dom.style.left = "-2000px";
39205             this.el.hide();
39206         //}else{
39207          //   this.collapsedEl.dom.style.left = "-2000px";
39208          //   this.collapsedEl.hide();
39209        // }
39210         this.visible = false;
39211         this.fireEvent("visibilitychange", this, false);
39212     },
39213
39214     /**
39215      * Shows this region if it was previously hidden.
39216      */
39217     show : function(){
39218         //if(!this.collapsed){
39219             this.el.show();
39220         //}else{
39221         //    this.collapsedEl.show();
39222        // }
39223         this.visible = true;
39224         this.fireEvent("visibilitychange", this, true);
39225     },
39226 /*
39227     closeClicked : function(){
39228         if(this.activePanel){
39229             this.remove(this.activePanel);
39230         }
39231     },
39232
39233     collapseClick : function(e){
39234         if(this.isSlid){
39235            e.stopPropagation();
39236            this.slideIn();
39237         }else{
39238            e.stopPropagation();
39239            this.slideOut();
39240         }
39241     },
39242 */
39243     /**
39244      * Collapses this region.
39245      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39246      */
39247     /*
39248     collapse : function(skipAnim, skipCheck = false){
39249         if(this.collapsed) {
39250             return;
39251         }
39252         
39253         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39254             
39255             this.collapsed = true;
39256             if(this.split){
39257                 this.split.el.hide();
39258             }
39259             if(this.config.animate && skipAnim !== true){
39260                 this.fireEvent("invalidated", this);
39261                 this.animateCollapse();
39262             }else{
39263                 this.el.setLocation(-20000,-20000);
39264                 this.el.hide();
39265                 this.collapsedEl.show();
39266                 this.fireEvent("collapsed", this);
39267                 this.fireEvent("invalidated", this);
39268             }
39269         }
39270         
39271     },
39272 */
39273     animateCollapse : function(){
39274         // overridden
39275     },
39276
39277     /**
39278      * Expands this region if it was previously collapsed.
39279      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39280      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39281      */
39282     /*
39283     expand : function(e, skipAnim){
39284         if(e) {
39285             e.stopPropagation();
39286         }
39287         if(!this.collapsed || this.el.hasActiveFx()) {
39288             return;
39289         }
39290         if(this.isSlid){
39291             this.afterSlideIn();
39292             skipAnim = true;
39293         }
39294         this.collapsed = false;
39295         if(this.config.animate && skipAnim !== true){
39296             this.animateExpand();
39297         }else{
39298             this.el.show();
39299             if(this.split){
39300                 this.split.el.show();
39301             }
39302             this.collapsedEl.setLocation(-2000,-2000);
39303             this.collapsedEl.hide();
39304             this.fireEvent("invalidated", this);
39305             this.fireEvent("expanded", this);
39306         }
39307     },
39308 */
39309     animateExpand : function(){
39310         // overridden
39311     },
39312
39313     initTabs : function()
39314     {
39315         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39316         
39317         var ts = new Roo.bootstrap.panel.Tabs({
39318             el: this.bodyEl.dom,
39319             region : this,
39320             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39321             disableTooltips: this.config.disableTabTips,
39322             toolbar : this.config.toolbar
39323         });
39324         
39325         if(this.config.hideTabs){
39326             ts.stripWrap.setDisplayed(false);
39327         }
39328         this.tabs = ts;
39329         ts.resizeTabs = this.config.resizeTabs === true;
39330         ts.minTabWidth = this.config.minTabWidth || 40;
39331         ts.maxTabWidth = this.config.maxTabWidth || 250;
39332         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39333         ts.monitorResize = false;
39334         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39335         ts.bodyEl.addClass('roo-layout-tabs-body');
39336         this.panels.each(this.initPanelAsTab, this);
39337     },
39338
39339     initPanelAsTab : function(panel){
39340         var ti = this.tabs.addTab(
39341             panel.getEl().id,
39342             panel.getTitle(),
39343             null,
39344             this.config.closeOnTab && panel.isClosable(),
39345             panel.tpl
39346         );
39347         if(panel.tabTip !== undefined){
39348             ti.setTooltip(panel.tabTip);
39349         }
39350         ti.on("activate", function(){
39351               this.setActivePanel(panel);
39352         }, this);
39353         
39354         if(this.config.closeOnTab){
39355             ti.on("beforeclose", function(t, e){
39356                 e.cancel = true;
39357                 this.remove(panel);
39358             }, this);
39359         }
39360         
39361         panel.tabItem = ti;
39362         
39363         return ti;
39364     },
39365
39366     updatePanelTitle : function(panel, title)
39367     {
39368         if(this.activePanel == panel){
39369             this.updateTitle(title);
39370         }
39371         if(this.tabs){
39372             var ti = this.tabs.getTab(panel.getEl().id);
39373             ti.setText(title);
39374             if(panel.tabTip !== undefined){
39375                 ti.setTooltip(panel.tabTip);
39376             }
39377         }
39378     },
39379
39380     updateTitle : function(title){
39381         if(this.titleTextEl && !this.config.title){
39382             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39383         }
39384     },
39385
39386     setActivePanel : function(panel)
39387     {
39388         panel = this.getPanel(panel);
39389         if(this.activePanel && this.activePanel != panel){
39390             if(this.activePanel.setActiveState(false) === false){
39391                 return;
39392             }
39393         }
39394         this.activePanel = panel;
39395         panel.setActiveState(true);
39396         if(this.panelSize){
39397             panel.setSize(this.panelSize.width, this.panelSize.height);
39398         }
39399         if(this.closeBtn){
39400             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39401         }
39402         this.updateTitle(panel.getTitle());
39403         if(this.tabs){
39404             this.fireEvent("invalidated", this);
39405         }
39406         this.fireEvent("panelactivated", this, panel);
39407     },
39408
39409     /**
39410      * Shows the specified panel.
39411      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39412      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39413      */
39414     showPanel : function(panel)
39415     {
39416         panel = this.getPanel(panel);
39417         if(panel){
39418             if(this.tabs){
39419                 var tab = this.tabs.getTab(panel.getEl().id);
39420                 if(tab.isHidden()){
39421                     this.tabs.unhideTab(tab.id);
39422                 }
39423                 tab.activate();
39424             }else{
39425                 this.setActivePanel(panel);
39426             }
39427         }
39428         return panel;
39429     },
39430
39431     /**
39432      * Get the active panel for this region.
39433      * @return {Roo.ContentPanel} The active panel or null
39434      */
39435     getActivePanel : function(){
39436         return this.activePanel;
39437     },
39438
39439     validateVisibility : function(){
39440         if(this.panels.getCount() < 1){
39441             this.updateTitle("&#160;");
39442             this.closeBtn.hide();
39443             this.hide();
39444         }else{
39445             if(!this.isVisible()){
39446                 this.show();
39447             }
39448         }
39449     },
39450
39451     /**
39452      * Adds the passed ContentPanel(s) to this region.
39453      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39454      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39455      */
39456     add : function(panel)
39457     {
39458         if(arguments.length > 1){
39459             for(var i = 0, len = arguments.length; i < len; i++) {
39460                 this.add(arguments[i]);
39461             }
39462             return null;
39463         }
39464         
39465         // if we have not been rendered yet, then we can not really do much of this..
39466         if (!this.bodyEl) {
39467             this.unrendered_panels.push(panel);
39468             return panel;
39469         }
39470         
39471         
39472         
39473         
39474         if(this.hasPanel(panel)){
39475             this.showPanel(panel);
39476             return panel;
39477         }
39478         panel.setRegion(this);
39479         this.panels.add(panel);
39480        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39481             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39482             // and hide them... ???
39483             this.bodyEl.dom.appendChild(panel.getEl().dom);
39484             if(panel.background !== true){
39485                 this.setActivePanel(panel);
39486             }
39487             this.fireEvent("paneladded", this, panel);
39488             return panel;
39489         }
39490         */
39491         if(!this.tabs){
39492             this.initTabs();
39493         }else{
39494             this.initPanelAsTab(panel);
39495         }
39496         
39497         
39498         if(panel.background !== true){
39499             this.tabs.activate(panel.getEl().id);
39500         }
39501         this.fireEvent("paneladded", this, panel);
39502         return panel;
39503     },
39504
39505     /**
39506      * Hides the tab for the specified panel.
39507      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39508      */
39509     hidePanel : function(panel){
39510         if(this.tabs && (panel = this.getPanel(panel))){
39511             this.tabs.hideTab(panel.getEl().id);
39512         }
39513     },
39514
39515     /**
39516      * Unhides the tab for a previously hidden panel.
39517      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39518      */
39519     unhidePanel : function(panel){
39520         if(this.tabs && (panel = this.getPanel(panel))){
39521             this.tabs.unhideTab(panel.getEl().id);
39522         }
39523     },
39524
39525     clearPanels : function(){
39526         while(this.panels.getCount() > 0){
39527              this.remove(this.panels.first());
39528         }
39529     },
39530
39531     /**
39532      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39533      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39534      * @param {Boolean} preservePanel Overrides the config preservePanel option
39535      * @return {Roo.ContentPanel} The panel that was removed
39536      */
39537     remove : function(panel, preservePanel)
39538     {
39539         panel = this.getPanel(panel);
39540         if(!panel){
39541             return null;
39542         }
39543         var e = {};
39544         this.fireEvent("beforeremove", this, panel, e);
39545         if(e.cancel === true){
39546             return null;
39547         }
39548         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39549         var panelId = panel.getId();
39550         this.panels.removeKey(panelId);
39551         if(preservePanel){
39552             document.body.appendChild(panel.getEl().dom);
39553         }
39554         if(this.tabs){
39555             this.tabs.removeTab(panel.getEl().id);
39556         }else if (!preservePanel){
39557             this.bodyEl.dom.removeChild(panel.getEl().dom);
39558         }
39559         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39560             var p = this.panels.first();
39561             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39562             tempEl.appendChild(p.getEl().dom);
39563             this.bodyEl.update("");
39564             this.bodyEl.dom.appendChild(p.getEl().dom);
39565             tempEl = null;
39566             this.updateTitle(p.getTitle());
39567             this.tabs = null;
39568             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39569             this.setActivePanel(p);
39570         }
39571         panel.setRegion(null);
39572         if(this.activePanel == panel){
39573             this.activePanel = null;
39574         }
39575         if(this.config.autoDestroy !== false && preservePanel !== true){
39576             try{panel.destroy();}catch(e){}
39577         }
39578         this.fireEvent("panelremoved", this, panel);
39579         return panel;
39580     },
39581
39582     /**
39583      * Returns the TabPanel component used by this region
39584      * @return {Roo.TabPanel}
39585      */
39586     getTabs : function(){
39587         return this.tabs;
39588     },
39589
39590     createTool : function(parentEl, className){
39591         var btn = Roo.DomHelper.append(parentEl, {
39592             tag: "div",
39593             cls: "x-layout-tools-button",
39594             children: [ {
39595                 tag: "div",
39596                 cls: "roo-layout-tools-button-inner " + className,
39597                 html: "&#160;"
39598             }]
39599         }, true);
39600         btn.addClassOnOver("roo-layout-tools-button-over");
39601         return btn;
39602     }
39603 });/*
39604  * Based on:
39605  * Ext JS Library 1.1.1
39606  * Copyright(c) 2006-2007, Ext JS, LLC.
39607  *
39608  * Originally Released Under LGPL - original licence link has changed is not relivant.
39609  *
39610  * Fork - LGPL
39611  * <script type="text/javascript">
39612  */
39613  
39614
39615
39616 /**
39617  * @class Roo.SplitLayoutRegion
39618  * @extends Roo.LayoutRegion
39619  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39620  */
39621 Roo.bootstrap.layout.Split = function(config){
39622     this.cursor = config.cursor;
39623     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39624 };
39625
39626 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39627 {
39628     splitTip : "Drag to resize.",
39629     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39630     useSplitTips : false,
39631
39632     applyConfig : function(config){
39633         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39634     },
39635     
39636     onRender : function(ctr,pos) {
39637         
39638         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39639         if(!this.config.split){
39640             return;
39641         }
39642         if(!this.split){
39643             
39644             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39645                             tag: "div",
39646                             id: this.el.id + "-split",
39647                             cls: "roo-layout-split roo-layout-split-"+this.position,
39648                             html: "&#160;"
39649             });
39650             /** The SplitBar for this region 
39651             * @type Roo.SplitBar */
39652             // does not exist yet...
39653             Roo.log([this.position, this.orientation]);
39654             
39655             this.split = new Roo.bootstrap.SplitBar({
39656                 dragElement : splitEl,
39657                 resizingElement: this.el,
39658                 orientation : this.orientation
39659             });
39660             
39661             this.split.on("moved", this.onSplitMove, this);
39662             this.split.useShim = this.config.useShim === true;
39663             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39664             if(this.useSplitTips){
39665                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39666             }
39667             //if(config.collapsible){
39668             //    this.split.el.on("dblclick", this.collapse,  this);
39669             //}
39670         }
39671         if(typeof this.config.minSize != "undefined"){
39672             this.split.minSize = this.config.minSize;
39673         }
39674         if(typeof this.config.maxSize != "undefined"){
39675             this.split.maxSize = this.config.maxSize;
39676         }
39677         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39678             this.hideSplitter();
39679         }
39680         
39681     },
39682
39683     getHMaxSize : function(){
39684          var cmax = this.config.maxSize || 10000;
39685          var center = this.mgr.getRegion("center");
39686          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39687     },
39688
39689     getVMaxSize : function(){
39690          var cmax = this.config.maxSize || 10000;
39691          var center = this.mgr.getRegion("center");
39692          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39693     },
39694
39695     onSplitMove : function(split, newSize){
39696         this.fireEvent("resized", this, newSize);
39697     },
39698     
39699     /** 
39700      * Returns the {@link Roo.SplitBar} for this region.
39701      * @return {Roo.SplitBar}
39702      */
39703     getSplitBar : function(){
39704         return this.split;
39705     },
39706     
39707     hide : function(){
39708         this.hideSplitter();
39709         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39710     },
39711
39712     hideSplitter : function(){
39713         if(this.split){
39714             this.split.el.setLocation(-2000,-2000);
39715             this.split.el.hide();
39716         }
39717     },
39718
39719     show : function(){
39720         if(this.split){
39721             this.split.el.show();
39722         }
39723         Roo.bootstrap.layout.Split.superclass.show.call(this);
39724     },
39725     
39726     beforeSlide: function(){
39727         if(Roo.isGecko){// firefox overflow auto bug workaround
39728             this.bodyEl.clip();
39729             if(this.tabs) {
39730                 this.tabs.bodyEl.clip();
39731             }
39732             if(this.activePanel){
39733                 this.activePanel.getEl().clip();
39734                 
39735                 if(this.activePanel.beforeSlide){
39736                     this.activePanel.beforeSlide();
39737                 }
39738             }
39739         }
39740     },
39741     
39742     afterSlide : function(){
39743         if(Roo.isGecko){// firefox overflow auto bug workaround
39744             this.bodyEl.unclip();
39745             if(this.tabs) {
39746                 this.tabs.bodyEl.unclip();
39747             }
39748             if(this.activePanel){
39749                 this.activePanel.getEl().unclip();
39750                 if(this.activePanel.afterSlide){
39751                     this.activePanel.afterSlide();
39752                 }
39753             }
39754         }
39755     },
39756
39757     initAutoHide : function(){
39758         if(this.autoHide !== false){
39759             if(!this.autoHideHd){
39760                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39761                 this.autoHideHd = {
39762                     "mouseout": function(e){
39763                         if(!e.within(this.el, true)){
39764                             st.delay(500);
39765                         }
39766                     },
39767                     "mouseover" : function(e){
39768                         st.cancel();
39769                     },
39770                     scope : this
39771                 };
39772             }
39773             this.el.on(this.autoHideHd);
39774         }
39775     },
39776
39777     clearAutoHide : function(){
39778         if(this.autoHide !== false){
39779             this.el.un("mouseout", this.autoHideHd.mouseout);
39780             this.el.un("mouseover", this.autoHideHd.mouseover);
39781         }
39782     },
39783
39784     clearMonitor : function(){
39785         Roo.get(document).un("click", this.slideInIf, this);
39786     },
39787
39788     // these names are backwards but not changed for compat
39789     slideOut : function(){
39790         if(this.isSlid || this.el.hasActiveFx()){
39791             return;
39792         }
39793         this.isSlid = true;
39794         if(this.collapseBtn){
39795             this.collapseBtn.hide();
39796         }
39797         this.closeBtnState = this.closeBtn.getStyle('display');
39798         this.closeBtn.hide();
39799         if(this.stickBtn){
39800             this.stickBtn.show();
39801         }
39802         this.el.show();
39803         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39804         this.beforeSlide();
39805         this.el.setStyle("z-index", 10001);
39806         this.el.slideIn(this.getSlideAnchor(), {
39807             callback: function(){
39808                 this.afterSlide();
39809                 this.initAutoHide();
39810                 Roo.get(document).on("click", this.slideInIf, this);
39811                 this.fireEvent("slideshow", this);
39812             },
39813             scope: this,
39814             block: true
39815         });
39816     },
39817
39818     afterSlideIn : function(){
39819         this.clearAutoHide();
39820         this.isSlid = false;
39821         this.clearMonitor();
39822         this.el.setStyle("z-index", "");
39823         if(this.collapseBtn){
39824             this.collapseBtn.show();
39825         }
39826         this.closeBtn.setStyle('display', this.closeBtnState);
39827         if(this.stickBtn){
39828             this.stickBtn.hide();
39829         }
39830         this.fireEvent("slidehide", this);
39831     },
39832
39833     slideIn : function(cb){
39834         if(!this.isSlid || this.el.hasActiveFx()){
39835             Roo.callback(cb);
39836             return;
39837         }
39838         this.isSlid = false;
39839         this.beforeSlide();
39840         this.el.slideOut(this.getSlideAnchor(), {
39841             callback: function(){
39842                 this.el.setLeftTop(-10000, -10000);
39843                 this.afterSlide();
39844                 this.afterSlideIn();
39845                 Roo.callback(cb);
39846             },
39847             scope: this,
39848             block: true
39849         });
39850     },
39851     
39852     slideInIf : function(e){
39853         if(!e.within(this.el)){
39854             this.slideIn();
39855         }
39856     },
39857
39858     animateCollapse : function(){
39859         this.beforeSlide();
39860         this.el.setStyle("z-index", 20000);
39861         var anchor = this.getSlideAnchor();
39862         this.el.slideOut(anchor, {
39863             callback : function(){
39864                 this.el.setStyle("z-index", "");
39865                 this.collapsedEl.slideIn(anchor, {duration:.3});
39866                 this.afterSlide();
39867                 this.el.setLocation(-10000,-10000);
39868                 this.el.hide();
39869                 this.fireEvent("collapsed", this);
39870             },
39871             scope: this,
39872             block: true
39873         });
39874     },
39875
39876     animateExpand : function(){
39877         this.beforeSlide();
39878         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39879         this.el.setStyle("z-index", 20000);
39880         this.collapsedEl.hide({
39881             duration:.1
39882         });
39883         this.el.slideIn(this.getSlideAnchor(), {
39884             callback : function(){
39885                 this.el.setStyle("z-index", "");
39886                 this.afterSlide();
39887                 if(this.split){
39888                     this.split.el.show();
39889                 }
39890                 this.fireEvent("invalidated", this);
39891                 this.fireEvent("expanded", this);
39892             },
39893             scope: this,
39894             block: true
39895         });
39896     },
39897
39898     anchors : {
39899         "west" : "left",
39900         "east" : "right",
39901         "north" : "top",
39902         "south" : "bottom"
39903     },
39904
39905     sanchors : {
39906         "west" : "l",
39907         "east" : "r",
39908         "north" : "t",
39909         "south" : "b"
39910     },
39911
39912     canchors : {
39913         "west" : "tl-tr",
39914         "east" : "tr-tl",
39915         "north" : "tl-bl",
39916         "south" : "bl-tl"
39917     },
39918
39919     getAnchor : function(){
39920         return this.anchors[this.position];
39921     },
39922
39923     getCollapseAnchor : function(){
39924         return this.canchors[this.position];
39925     },
39926
39927     getSlideAnchor : function(){
39928         return this.sanchors[this.position];
39929     },
39930
39931     getAlignAdj : function(){
39932         var cm = this.cmargins;
39933         switch(this.position){
39934             case "west":
39935                 return [0, 0];
39936             break;
39937             case "east":
39938                 return [0, 0];
39939             break;
39940             case "north":
39941                 return [0, 0];
39942             break;
39943             case "south":
39944                 return [0, 0];
39945             break;
39946         }
39947     },
39948
39949     getExpandAdj : function(){
39950         var c = this.collapsedEl, cm = this.cmargins;
39951         switch(this.position){
39952             case "west":
39953                 return [-(cm.right+c.getWidth()+cm.left), 0];
39954             break;
39955             case "east":
39956                 return [cm.right+c.getWidth()+cm.left, 0];
39957             break;
39958             case "north":
39959                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39960             break;
39961             case "south":
39962                 return [0, cm.top+cm.bottom+c.getHeight()];
39963             break;
39964         }
39965     }
39966 });/*
39967  * Based on:
39968  * Ext JS Library 1.1.1
39969  * Copyright(c) 2006-2007, Ext JS, LLC.
39970  *
39971  * Originally Released Under LGPL - original licence link has changed is not relivant.
39972  *
39973  * Fork - LGPL
39974  * <script type="text/javascript">
39975  */
39976 /*
39977  * These classes are private internal classes
39978  */
39979 Roo.bootstrap.layout.Center = function(config){
39980     config.region = "center";
39981     Roo.bootstrap.layout.Region.call(this, config);
39982     this.visible = true;
39983     this.minWidth = config.minWidth || 20;
39984     this.minHeight = config.minHeight || 20;
39985 };
39986
39987 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39988     hide : function(){
39989         // center panel can't be hidden
39990     },
39991     
39992     show : function(){
39993         // center panel can't be hidden
39994     },
39995     
39996     getMinWidth: function(){
39997         return this.minWidth;
39998     },
39999     
40000     getMinHeight: function(){
40001         return this.minHeight;
40002     }
40003 });
40004
40005
40006
40007
40008  
40009
40010
40011
40012
40013
40014
40015 Roo.bootstrap.layout.North = function(config)
40016 {
40017     config.region = 'north';
40018     config.cursor = 'n-resize';
40019     
40020     Roo.bootstrap.layout.Split.call(this, config);
40021     
40022     
40023     if(this.split){
40024         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40025         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40026         this.split.el.addClass("roo-layout-split-v");
40027     }
40028     //var size = config.initialSize || config.height;
40029     //if(this.el && typeof size != "undefined"){
40030     //    this.el.setHeight(size);
40031     //}
40032 };
40033 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40034 {
40035     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40036      
40037      
40038     onRender : function(ctr, pos)
40039     {
40040         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40041         var size = this.config.initialSize || this.config.height;
40042         if(this.el && typeof size != "undefined"){
40043             this.el.setHeight(size);
40044         }
40045     
40046     },
40047     
40048     getBox : function(){
40049         if(this.collapsed){
40050             return this.collapsedEl.getBox();
40051         }
40052         var box = this.el.getBox();
40053         if(this.split){
40054             box.height += this.split.el.getHeight();
40055         }
40056         return box;
40057     },
40058     
40059     updateBox : function(box){
40060         if(this.split && !this.collapsed){
40061             box.height -= this.split.el.getHeight();
40062             this.split.el.setLeft(box.x);
40063             this.split.el.setTop(box.y+box.height);
40064             this.split.el.setWidth(box.width);
40065         }
40066         if(this.collapsed){
40067             this.updateBody(box.width, null);
40068         }
40069         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40070     }
40071 });
40072
40073
40074
40075
40076
40077 Roo.bootstrap.layout.South = function(config){
40078     config.region = 'south';
40079     config.cursor = 's-resize';
40080     Roo.bootstrap.layout.Split.call(this, config);
40081     if(this.split){
40082         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40083         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40084         this.split.el.addClass("roo-layout-split-v");
40085     }
40086     
40087 };
40088
40089 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40090     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40091     
40092     onRender : function(ctr, pos)
40093     {
40094         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40095         var size = this.config.initialSize || this.config.height;
40096         if(this.el && typeof size != "undefined"){
40097             this.el.setHeight(size);
40098         }
40099     
40100     },
40101     
40102     getBox : function(){
40103         if(this.collapsed){
40104             return this.collapsedEl.getBox();
40105         }
40106         var box = this.el.getBox();
40107         if(this.split){
40108             var sh = this.split.el.getHeight();
40109             box.height += sh;
40110             box.y -= sh;
40111         }
40112         return box;
40113     },
40114     
40115     updateBox : function(box){
40116         if(this.split && !this.collapsed){
40117             var sh = this.split.el.getHeight();
40118             box.height -= sh;
40119             box.y += sh;
40120             this.split.el.setLeft(box.x);
40121             this.split.el.setTop(box.y-sh);
40122             this.split.el.setWidth(box.width);
40123         }
40124         if(this.collapsed){
40125             this.updateBody(box.width, null);
40126         }
40127         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40128     }
40129 });
40130
40131 Roo.bootstrap.layout.East = function(config){
40132     config.region = "east";
40133     config.cursor = "e-resize";
40134     Roo.bootstrap.layout.Split.call(this, config);
40135     if(this.split){
40136         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40137         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40138         this.split.el.addClass("roo-layout-split-h");
40139     }
40140     
40141 };
40142 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40143     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40144     
40145     onRender : function(ctr, pos)
40146     {
40147         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40148         var size = this.config.initialSize || this.config.width;
40149         if(this.el && typeof size != "undefined"){
40150             this.el.setWidth(size);
40151         }
40152     
40153     },
40154     
40155     getBox : function(){
40156         if(this.collapsed){
40157             return this.collapsedEl.getBox();
40158         }
40159         var box = this.el.getBox();
40160         if(this.split){
40161             var sw = this.split.el.getWidth();
40162             box.width += sw;
40163             box.x -= sw;
40164         }
40165         return box;
40166     },
40167
40168     updateBox : function(box){
40169         if(this.split && !this.collapsed){
40170             var sw = this.split.el.getWidth();
40171             box.width -= sw;
40172             this.split.el.setLeft(box.x);
40173             this.split.el.setTop(box.y);
40174             this.split.el.setHeight(box.height);
40175             box.x += sw;
40176         }
40177         if(this.collapsed){
40178             this.updateBody(null, box.height);
40179         }
40180         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40181     }
40182 });
40183
40184 Roo.bootstrap.layout.West = function(config){
40185     config.region = "west";
40186     config.cursor = "w-resize";
40187     
40188     Roo.bootstrap.layout.Split.call(this, config);
40189     if(this.split){
40190         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40191         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40192         this.split.el.addClass("roo-layout-split-h");
40193     }
40194     
40195 };
40196 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40197     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40198     
40199     onRender: function(ctr, pos)
40200     {
40201         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40202         var size = this.config.initialSize || this.config.width;
40203         if(typeof size != "undefined"){
40204             this.el.setWidth(size);
40205         }
40206     },
40207     
40208     getBox : function(){
40209         if(this.collapsed){
40210             return this.collapsedEl.getBox();
40211         }
40212         var box = this.el.getBox();
40213         if (box.width == 0) {
40214             box.width = this.config.width; // kludge?
40215         }
40216         if(this.split){
40217             box.width += this.split.el.getWidth();
40218         }
40219         return box;
40220     },
40221     
40222     updateBox : function(box){
40223         if(this.split && !this.collapsed){
40224             var sw = this.split.el.getWidth();
40225             box.width -= sw;
40226             this.split.el.setLeft(box.x+box.width);
40227             this.split.el.setTop(box.y);
40228             this.split.el.setHeight(box.height);
40229         }
40230         if(this.collapsed){
40231             this.updateBody(null, box.height);
40232         }
40233         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40234     }
40235 });/*
40236  * Based on:
40237  * Ext JS Library 1.1.1
40238  * Copyright(c) 2006-2007, Ext JS, LLC.
40239  *
40240  * Originally Released Under LGPL - original licence link has changed is not relivant.
40241  *
40242  * Fork - LGPL
40243  * <script type="text/javascript">
40244  */
40245 /**
40246  * @class Roo.bootstrap.paenl.Content
40247  * @extends Roo.util.Observable
40248  * @children Roo.bootstrap.Component
40249  * @parent builder Roo.bootstrap.layout.Border
40250  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40251  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40252  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40253  * @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
40254  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40255  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40256  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40257  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40258  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40259  * @cfg {String} title          The title for this panel
40260  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40261  * @cfg {String} url            Calls {@link #setUrl} with this value
40262  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40263  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40264  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40265  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40266  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40267  * @cfg {Boolean} badges render the badges
40268  * @cfg {String} cls  extra classes to use  
40269  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40270  
40271  * @constructor
40272  * Create a new ContentPanel.
40273  * @param {String/Object} config A string to set only the title or a config object
40274  
40275  */
40276 Roo.bootstrap.panel.Content = function( config){
40277     
40278     this.tpl = config.tpl || false;
40279     
40280     var el = config.el;
40281     var content = config.content;
40282
40283     if(config.autoCreate){ // xtype is available if this is called from factory
40284         el = Roo.id();
40285     }
40286     this.el = Roo.get(el);
40287     if(!this.el && config && config.autoCreate){
40288         if(typeof config.autoCreate == "object"){
40289             if(!config.autoCreate.id){
40290                 config.autoCreate.id = config.id||el;
40291             }
40292             this.el = Roo.DomHelper.append(document.body,
40293                         config.autoCreate, true);
40294         }else{
40295             var elcfg =  {
40296                 tag: "div",
40297                 cls: (config.cls || '') +
40298                     (config.background ? ' bg-' + config.background : '') +
40299                     " roo-layout-inactive-content",
40300                 id: config.id||el
40301             };
40302             if (config.iframe) {
40303                 elcfg.cn = [
40304                     {
40305                         tag : 'iframe',
40306                         style : 'border: 0px',
40307                         src : 'about:blank'
40308                     }
40309                 ];
40310             }
40311               
40312             if (config.html) {
40313                 elcfg.html = config.html;
40314                 
40315             }
40316                         
40317             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40318             if (config.iframe) {
40319                 this.iframeEl = this.el.select('iframe',true).first();
40320             }
40321             
40322         }
40323     } 
40324     this.closable = false;
40325     this.loaded = false;
40326     this.active = false;
40327    
40328       
40329     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40330         
40331         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40332         
40333         this.wrapEl = this.el; //this.el.wrap();
40334         var ti = [];
40335         if (config.toolbar.items) {
40336             ti = config.toolbar.items ;
40337             delete config.toolbar.items ;
40338         }
40339         
40340         var nitems = [];
40341         this.toolbar.render(this.wrapEl, 'before');
40342         for(var i =0;i < ti.length;i++) {
40343           //  Roo.log(['add child', items[i]]);
40344             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40345         }
40346         this.toolbar.items = nitems;
40347         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40348         delete config.toolbar;
40349         
40350     }
40351     /*
40352     // xtype created footer. - not sure if will work as we normally have to render first..
40353     if (this.footer && !this.footer.el && this.footer.xtype) {
40354         if (!this.wrapEl) {
40355             this.wrapEl = this.el.wrap();
40356         }
40357     
40358         this.footer.container = this.wrapEl.createChild();
40359          
40360         this.footer = Roo.factory(this.footer, Roo);
40361         
40362     }
40363     */
40364     
40365      if(typeof config == "string"){
40366         this.title = config;
40367     }else{
40368         Roo.apply(this, config);
40369     }
40370     
40371     if(this.resizeEl){
40372         this.resizeEl = Roo.get(this.resizeEl, true);
40373     }else{
40374         this.resizeEl = this.el;
40375     }
40376     // handle view.xtype
40377     
40378  
40379     
40380     
40381     this.addEvents({
40382         /**
40383          * @event activate
40384          * Fires when this panel is activated. 
40385          * @param {Roo.ContentPanel} this
40386          */
40387         "activate" : true,
40388         /**
40389          * @event deactivate
40390          * Fires when this panel is activated. 
40391          * @param {Roo.ContentPanel} this
40392          */
40393         "deactivate" : true,
40394
40395         /**
40396          * @event resize
40397          * Fires when this panel is resized if fitToFrame is true.
40398          * @param {Roo.ContentPanel} this
40399          * @param {Number} width The width after any component adjustments
40400          * @param {Number} height The height after any component adjustments
40401          */
40402         "resize" : true,
40403         
40404          /**
40405          * @event render
40406          * Fires when this tab is created
40407          * @param {Roo.ContentPanel} this
40408          */
40409         "render" : true,
40410         
40411           /**
40412          * @event scroll
40413          * Fires when this content is scrolled
40414          * @param {Roo.ContentPanel} this
40415          * @param {Event} scrollEvent
40416          */
40417         "scroll" : true
40418         
40419         
40420         
40421     });
40422     
40423
40424     
40425     
40426     if(this.autoScroll && !this.iframe){
40427         this.resizeEl.setStyle("overflow", "auto");
40428         this.resizeEl.on('scroll', this.onScroll, this);
40429     } else {
40430         // fix randome scrolling
40431         //this.el.on('scroll', function() {
40432         //    Roo.log('fix random scolling');
40433         //    this.scrollTo('top',0); 
40434         //});
40435     }
40436     content = content || this.content;
40437     if(content){
40438         this.setContent(content);
40439     }
40440     if(config && config.url){
40441         this.setUrl(this.url, this.params, this.loadOnce);
40442     }
40443     
40444     
40445     
40446     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40447     
40448     if (this.view && typeof(this.view.xtype) != 'undefined') {
40449         this.view.el = this.el.appendChild(document.createElement("div"));
40450         this.view = Roo.factory(this.view); 
40451         this.view.render  &&  this.view.render(false, '');  
40452     }
40453     
40454     
40455     this.fireEvent('render', this);
40456 };
40457
40458 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40459     
40460     cls : '',
40461     background : '',
40462     
40463     tabTip : '',
40464     
40465     iframe : false,
40466     iframeEl : false,
40467     
40468     /* Resize Element - use this to work out scroll etc. */
40469     resizeEl : false,
40470     
40471     setRegion : function(region){
40472         this.region = region;
40473         this.setActiveClass(region && !this.background);
40474     },
40475     
40476     
40477     setActiveClass: function(state)
40478     {
40479         if(state){
40480            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40481            this.el.setStyle('position','relative');
40482         }else{
40483            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40484            this.el.setStyle('position', 'absolute');
40485         } 
40486     },
40487     
40488     /**
40489      * Returns the toolbar for this Panel if one was configured. 
40490      * @return {Roo.Toolbar} 
40491      */
40492     getToolbar : function(){
40493         return this.toolbar;
40494     },
40495     
40496     setActiveState : function(active)
40497     {
40498         this.active = active;
40499         this.setActiveClass(active);
40500         if(!active){
40501             if(this.fireEvent("deactivate", this) === false){
40502                 return false;
40503             }
40504             return true;
40505         }
40506         this.fireEvent("activate", this);
40507         return true;
40508     },
40509     /**
40510      * Updates this panel's element (not for iframe)
40511      * @param {String} content The new content
40512      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40513     */
40514     setContent : function(content, loadScripts){
40515         if (this.iframe) {
40516             return;
40517         }
40518         
40519         this.el.update(content, loadScripts);
40520     },
40521
40522     ignoreResize : function(w, h)
40523     {
40524         return false; // always resize?
40525         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40526             return true;
40527         }else{
40528             this.lastSize = {width: w, height: h};
40529             return false;
40530         }
40531     },
40532     /**
40533      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40534      * @return {Roo.UpdateManager} The UpdateManager
40535      */
40536     getUpdateManager : function(){
40537         if (this.iframe) {
40538             return false;
40539         }
40540         return this.el.getUpdateManager();
40541     },
40542      /**
40543      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40544      * Does not work with IFRAME contents
40545      * @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:
40546 <pre><code>
40547 panel.load({
40548     url: "your-url.php",
40549     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40550     callback: yourFunction,
40551     scope: yourObject, //(optional scope)
40552     discardUrl: false,
40553     nocache: false,
40554     text: "Loading...",
40555     timeout: 30,
40556     scripts: false
40557 });
40558 </code></pre>
40559      
40560      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40561      * 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.
40562      * @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}
40563      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40564      * @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.
40565      * @return {Roo.ContentPanel} this
40566      */
40567     load : function(){
40568         
40569         if (this.iframe) {
40570             return this;
40571         }
40572         
40573         var um = this.el.getUpdateManager();
40574         um.update.apply(um, arguments);
40575         return this;
40576     },
40577
40578
40579     /**
40580      * 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.
40581      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40582      * @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)
40583      * @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)
40584      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40585      */
40586     setUrl : function(url, params, loadOnce){
40587         if (this.iframe) {
40588             this.iframeEl.dom.src = url;
40589             return false;
40590         }
40591         
40592         if(this.refreshDelegate){
40593             this.removeListener("activate", this.refreshDelegate);
40594         }
40595         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40596         this.on("activate", this.refreshDelegate);
40597         return this.el.getUpdateManager();
40598     },
40599     
40600     _handleRefresh : function(url, params, loadOnce){
40601         if(!loadOnce || !this.loaded){
40602             var updater = this.el.getUpdateManager();
40603             updater.update(url, params, this._setLoaded.createDelegate(this));
40604         }
40605     },
40606     
40607     _setLoaded : function(){
40608         this.loaded = true;
40609     }, 
40610     
40611     /**
40612      * Returns this panel's id
40613      * @return {String} 
40614      */
40615     getId : function(){
40616         return this.el.id;
40617     },
40618     
40619     /** 
40620      * Returns this panel's element - used by regiosn to add.
40621      * @return {Roo.Element} 
40622      */
40623     getEl : function(){
40624         return this.wrapEl || this.el;
40625     },
40626     
40627    
40628     
40629     adjustForComponents : function(width, height)
40630     {
40631         //Roo.log('adjustForComponents ');
40632         if(this.resizeEl != this.el){
40633             width -= this.el.getFrameWidth('lr');
40634             height -= this.el.getFrameWidth('tb');
40635         }
40636         if(this.toolbar){
40637             var te = this.toolbar.getEl();
40638             te.setWidth(width);
40639             height -= te.getHeight();
40640         }
40641         if(this.footer){
40642             var te = this.footer.getEl();
40643             te.setWidth(width);
40644             height -= te.getHeight();
40645         }
40646         
40647         
40648         if(this.adjustments){
40649             width += this.adjustments[0];
40650             height += this.adjustments[1];
40651         }
40652         return {"width": width, "height": height};
40653     },
40654     
40655     setSize : function(width, height){
40656         if(this.fitToFrame && !this.ignoreResize(width, height)){
40657             if(this.fitContainer && this.resizeEl != this.el){
40658                 this.el.setSize(width, height);
40659             }
40660             var size = this.adjustForComponents(width, height);
40661             if (this.iframe) {
40662                 this.iframeEl.setSize(width,height);
40663             }
40664             
40665             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40666             this.fireEvent('resize', this, size.width, size.height);
40667             
40668             
40669         }
40670     },
40671     
40672     /**
40673      * Returns this panel's title
40674      * @return {String} 
40675      */
40676     getTitle : function(){
40677         
40678         if (typeof(this.title) != 'object') {
40679             return this.title;
40680         }
40681         
40682         var t = '';
40683         for (var k in this.title) {
40684             if (!this.title.hasOwnProperty(k)) {
40685                 continue;
40686             }
40687             
40688             if (k.indexOf('-') >= 0) {
40689                 var s = k.split('-');
40690                 for (var i = 0; i<s.length; i++) {
40691                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40692                 }
40693             } else {
40694                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40695             }
40696         }
40697         return t;
40698     },
40699     
40700     /**
40701      * Set this panel's title
40702      * @param {String} title
40703      */
40704     setTitle : function(title){
40705         this.title = title;
40706         if(this.region){
40707             this.region.updatePanelTitle(this, title);
40708         }
40709     },
40710     
40711     /**
40712      * Returns true is this panel was configured to be closable
40713      * @return {Boolean} 
40714      */
40715     isClosable : function(){
40716         return this.closable;
40717     },
40718     
40719     beforeSlide : function(){
40720         this.el.clip();
40721         this.resizeEl.clip();
40722     },
40723     
40724     afterSlide : function(){
40725         this.el.unclip();
40726         this.resizeEl.unclip();
40727     },
40728     
40729     /**
40730      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40731      *   Will fail silently if the {@link #setUrl} method has not been called.
40732      *   This does not activate the panel, just updates its content.
40733      */
40734     refresh : function(){
40735         if(this.refreshDelegate){
40736            this.loaded = false;
40737            this.refreshDelegate();
40738         }
40739     },
40740     
40741     /**
40742      * Destroys this panel
40743      */
40744     destroy : function(){
40745         this.el.removeAllListeners();
40746         var tempEl = document.createElement("span");
40747         tempEl.appendChild(this.el.dom);
40748         tempEl.innerHTML = "";
40749         this.el.remove();
40750         this.el = null;
40751     },
40752     
40753     /**
40754      * form - if the content panel contains a form - this is a reference to it.
40755      * @type {Roo.form.Form}
40756      */
40757     form : false,
40758     /**
40759      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40760      *    This contains a reference to it.
40761      * @type {Roo.View}
40762      */
40763     view : false,
40764     
40765       /**
40766      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40767      * <pre><code>
40768
40769 layout.addxtype({
40770        xtype : 'Form',
40771        items: [ .... ]
40772    }
40773 );
40774
40775 </code></pre>
40776      * @param {Object} cfg Xtype definition of item to add.
40777      */
40778     
40779     
40780     getChildContainer: function () {
40781         return this.getEl();
40782     },
40783     
40784     
40785     onScroll : function(e)
40786     {
40787         this.fireEvent('scroll', this, e);
40788     }
40789     
40790     
40791     /*
40792         var  ret = new Roo.factory(cfg);
40793         return ret;
40794         
40795         
40796         // add form..
40797         if (cfg.xtype.match(/^Form$/)) {
40798             
40799             var el;
40800             //if (this.footer) {
40801             //    el = this.footer.container.insertSibling(false, 'before');
40802             //} else {
40803                 el = this.el.createChild();
40804             //}
40805
40806             this.form = new  Roo.form.Form(cfg);
40807             
40808             
40809             if ( this.form.allItems.length) {
40810                 this.form.render(el.dom);
40811             }
40812             return this.form;
40813         }
40814         // should only have one of theses..
40815         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40816             // views.. should not be just added - used named prop 'view''
40817             
40818             cfg.el = this.el.appendChild(document.createElement("div"));
40819             // factory?
40820             
40821             var ret = new Roo.factory(cfg);
40822              
40823              ret.render && ret.render(false, ''); // render blank..
40824             this.view = ret;
40825             return ret;
40826         }
40827         return false;
40828     }
40829     \*/
40830 });
40831  
40832 /**
40833  * @class Roo.bootstrap.panel.Grid
40834  * @extends Roo.bootstrap.panel.Content
40835  * @constructor
40836  * Create a new GridPanel.
40837  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40838  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40839  * @param {Object} config A the config object
40840   
40841  */
40842
40843
40844
40845 Roo.bootstrap.panel.Grid = function(config)
40846 {
40847     
40848       
40849     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40850         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40851
40852     config.el = this.wrapper;
40853     //this.el = this.wrapper;
40854     
40855       if (config.container) {
40856         // ctor'ed from a Border/panel.grid
40857         
40858         
40859         this.wrapper.setStyle("overflow", "hidden");
40860         this.wrapper.addClass('roo-grid-container');
40861
40862     }
40863     
40864     
40865     if(config.toolbar){
40866         var tool_el = this.wrapper.createChild();    
40867         this.toolbar = Roo.factory(config.toolbar);
40868         var ti = [];
40869         if (config.toolbar.items) {
40870             ti = config.toolbar.items ;
40871             delete config.toolbar.items ;
40872         }
40873         
40874         var nitems = [];
40875         this.toolbar.render(tool_el);
40876         for(var i =0;i < ti.length;i++) {
40877           //  Roo.log(['add child', items[i]]);
40878             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40879         }
40880         this.toolbar.items = nitems;
40881         
40882         delete config.toolbar;
40883     }
40884     
40885     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40886     config.grid.scrollBody = true;;
40887     config.grid.monitorWindowResize = false; // turn off autosizing
40888     config.grid.autoHeight = false;
40889     config.grid.autoWidth = false;
40890     
40891     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40892     
40893     if (config.background) {
40894         // render grid on panel activation (if panel background)
40895         this.on('activate', function(gp) {
40896             if (!gp.grid.rendered) {
40897                 gp.grid.render(this.wrapper);
40898                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40899             }
40900         });
40901             
40902     } else {
40903         this.grid.render(this.wrapper);
40904         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40905
40906     }
40907     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40908     // ??? needed ??? config.el = this.wrapper;
40909     
40910     
40911     
40912   
40913     // xtype created footer. - not sure if will work as we normally have to render first..
40914     if (this.footer && !this.footer.el && this.footer.xtype) {
40915         
40916         var ctr = this.grid.getView().getFooterPanel(true);
40917         this.footer.dataSource = this.grid.dataSource;
40918         this.footer = Roo.factory(this.footer, Roo);
40919         this.footer.render(ctr);
40920         
40921     }
40922     
40923     
40924     
40925     
40926      
40927 };
40928
40929 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40930 {
40931     // private
40932     is_resizing : false,
40933     
40934     getId : function(){
40935         return this.grid.id;
40936     },
40937     
40938     /**
40939      * Returns the grid for this panel
40940      * @return {Roo.bootstrap.Table} 
40941      */
40942     getGrid : function(){
40943         return this.grid;    
40944     },
40945     
40946     setSize : function(width, height)
40947     {
40948         if (this.is_resizing) {
40949             return;
40950         
40951         }
40952         this.is_resizing = true;
40953         if(!this.ignoreResize(width, height)){
40954             var grid = this.grid;
40955             var size = this.adjustForComponents(width, height);
40956             // tfoot is not a footer?
40957           
40958             
40959             var gridel = grid.getGridEl();
40960             gridel.setSize(size.width, size.height);
40961             
40962             var tbd = grid.getGridEl().select('tbody', true).first();
40963             var thd = grid.getGridEl().select('thead',true).first();
40964             var tbf= grid.getGridEl().select('tfoot', true).first();
40965
40966             if (tbf) {
40967                 size.height -= tbf.getHeight();
40968             }
40969             if (thd) {
40970                 size.height -= thd.getHeight();
40971             }
40972             
40973             tbd.setSize(size.width, size.height );
40974             // this is for the account management tab -seems to work there.
40975             var thd = grid.getGridEl().select('thead',true).first();
40976             //if (tbd) {
40977             //    tbd.setSize(size.width, size.height - thd.getHeight());
40978             //}
40979              
40980             grid.autoSize();
40981         }
40982         this.is_resizing = false;
40983     },
40984      
40985     
40986     
40987     beforeSlide : function(){
40988         this.grid.getView().scroller.clip();
40989     },
40990     
40991     afterSlide : function(){
40992         this.grid.getView().scroller.unclip();
40993     },
40994     
40995     destroy : function(){
40996         this.grid.destroy();
40997         delete this.grid;
40998         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40999     }
41000 });
41001
41002 /**
41003  * @class Roo.bootstrap.panel.Nest
41004  * @extends Roo.bootstrap.panel.Content
41005  * @constructor
41006  * Create a new Panel, that can contain a layout.Border.
41007  * 
41008  * 
41009  * @param {String/Object} config A string to set only the title or a config object
41010  */
41011 Roo.bootstrap.panel.Nest = function(config)
41012 {
41013     // construct with only one argument..
41014     /* FIXME - implement nicer consturctors
41015     if (layout.layout) {
41016         config = layout;
41017         layout = config.layout;
41018         delete config.layout;
41019     }
41020     if (layout.xtype && !layout.getEl) {
41021         // then layout needs constructing..
41022         layout = Roo.factory(layout, Roo);
41023     }
41024     */
41025     
41026     config.el =  config.layout.getEl();
41027     
41028     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41029     
41030     config.layout.monitorWindowResize = false; // turn off autosizing
41031     this.layout = config.layout;
41032     this.layout.getEl().addClass("roo-layout-nested-layout");
41033     this.layout.parent = this;
41034     
41035     
41036     
41037     
41038 };
41039
41040 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41041     /**
41042     * @cfg {Roo.BorderLayout} layout The layout for this panel
41043     */
41044     layout : false,
41045
41046     setSize : function(width, height){
41047         if(!this.ignoreResize(width, height)){
41048             var size = this.adjustForComponents(width, height);
41049             var el = this.layout.getEl();
41050             if (size.height < 1) {
41051                 el.setWidth(size.width);   
41052             } else {
41053                 el.setSize(size.width, size.height);
41054             }
41055             var touch = el.dom.offsetWidth;
41056             this.layout.layout();
41057             // ie requires a double layout on the first pass
41058             if(Roo.isIE && !this.initialized){
41059                 this.initialized = true;
41060                 this.layout.layout();
41061             }
41062         }
41063     },
41064     
41065     // activate all subpanels if not currently active..
41066     
41067     setActiveState : function(active){
41068         this.active = active;
41069         this.setActiveClass(active);
41070         
41071         if(!active){
41072             this.fireEvent("deactivate", this);
41073             return;
41074         }
41075         
41076         this.fireEvent("activate", this);
41077         // not sure if this should happen before or after..
41078         if (!this.layout) {
41079             return; // should not happen..
41080         }
41081         var reg = false;
41082         for (var r in this.layout.regions) {
41083             reg = this.layout.getRegion(r);
41084             if (reg.getActivePanel()) {
41085                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41086                 reg.setActivePanel(reg.getActivePanel());
41087                 continue;
41088             }
41089             if (!reg.panels.length) {
41090                 continue;
41091             }
41092             reg.showPanel(reg.getPanel(0));
41093         }
41094         
41095         
41096         
41097         
41098     },
41099     
41100     /**
41101      * Returns the nested BorderLayout for this panel
41102      * @return {Roo.BorderLayout} 
41103      */
41104     getLayout : function(){
41105         return this.layout;
41106     },
41107     
41108      /**
41109      * Adds a xtype elements to the layout of the nested panel
41110      * <pre><code>
41111
41112 panel.addxtype({
41113        xtype : 'ContentPanel',
41114        region: 'west',
41115        items: [ .... ]
41116    }
41117 );
41118
41119 panel.addxtype({
41120         xtype : 'NestedLayoutPanel',
41121         region: 'west',
41122         layout: {
41123            center: { },
41124            west: { }   
41125         },
41126         items : [ ... list of content panels or nested layout panels.. ]
41127    }
41128 );
41129 </code></pre>
41130      * @param {Object} cfg Xtype definition of item to add.
41131      */
41132     addxtype : function(cfg) {
41133         return this.layout.addxtype(cfg);
41134     
41135     }
41136 });/*
41137  * Based on:
41138  * Ext JS Library 1.1.1
41139  * Copyright(c) 2006-2007, Ext JS, LLC.
41140  *
41141  * Originally Released Under LGPL - original licence link has changed is not relivant.
41142  *
41143  * Fork - LGPL
41144  * <script type="text/javascript">
41145  */
41146 /**
41147  * @class Roo.TabPanel
41148  * @extends Roo.util.Observable
41149  * A lightweight tab container.
41150  * <br><br>
41151  * Usage:
41152  * <pre><code>
41153 // basic tabs 1, built from existing content
41154 var tabs = new Roo.TabPanel("tabs1");
41155 tabs.addTab("script", "View Script");
41156 tabs.addTab("markup", "View Markup");
41157 tabs.activate("script");
41158
41159 // more advanced tabs, built from javascript
41160 var jtabs = new Roo.TabPanel("jtabs");
41161 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41162
41163 // set up the UpdateManager
41164 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41165 var updater = tab2.getUpdateManager();
41166 updater.setDefaultUrl("ajax1.htm");
41167 tab2.on('activate', updater.refresh, updater, true);
41168
41169 // Use setUrl for Ajax loading
41170 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41171 tab3.setUrl("ajax2.htm", null, true);
41172
41173 // Disabled tab
41174 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41175 tab4.disable();
41176
41177 jtabs.activate("jtabs-1");
41178  * </code></pre>
41179  * @constructor
41180  * Create a new TabPanel.
41181  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41182  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41183  */
41184 Roo.bootstrap.panel.Tabs = function(config){
41185     /**
41186     * The container element for this TabPanel.
41187     * @type Roo.Element
41188     */
41189     this.el = Roo.get(config.el);
41190     delete config.el;
41191     if(config){
41192         if(typeof config == "boolean"){
41193             this.tabPosition = config ? "bottom" : "top";
41194         }else{
41195             Roo.apply(this, config);
41196         }
41197     }
41198     
41199     if(this.tabPosition == "bottom"){
41200         // if tabs are at the bottom = create the body first.
41201         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41202         this.el.addClass("roo-tabs-bottom");
41203     }
41204     // next create the tabs holders
41205     
41206     if (this.tabPosition == "west"){
41207         
41208         var reg = this.region; // fake it..
41209         while (reg) {
41210             if (!reg.mgr.parent) {
41211                 break;
41212             }
41213             reg = reg.mgr.parent.region;
41214         }
41215         Roo.log("got nest?");
41216         Roo.log(reg);
41217         if (reg.mgr.getRegion('west')) {
41218             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41219             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41220             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41221             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41222             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41223         
41224             
41225         }
41226         
41227         
41228     } else {
41229      
41230         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41231         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41232         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41233         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41234     }
41235     
41236     
41237     if(Roo.isIE){
41238         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41239     }
41240     
41241     // finally - if tabs are at the top, then create the body last..
41242     if(this.tabPosition != "bottom"){
41243         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41244          * @type Roo.Element
41245          */
41246         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41247         this.el.addClass("roo-tabs-top");
41248     }
41249     this.items = [];
41250
41251     this.bodyEl.setStyle("position", "relative");
41252
41253     this.active = null;
41254     this.activateDelegate = this.activate.createDelegate(this);
41255
41256     this.addEvents({
41257         /**
41258          * @event tabchange
41259          * Fires when the active tab changes
41260          * @param {Roo.TabPanel} this
41261          * @param {Roo.TabPanelItem} activePanel The new active tab
41262          */
41263         "tabchange": true,
41264         /**
41265          * @event beforetabchange
41266          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41267          * @param {Roo.TabPanel} this
41268          * @param {Object} e Set cancel to true on this object to cancel the tab change
41269          * @param {Roo.TabPanelItem} tab The tab being changed to
41270          */
41271         "beforetabchange" : true
41272     });
41273
41274     Roo.EventManager.onWindowResize(this.onResize, this);
41275     this.cpad = this.el.getPadding("lr");
41276     this.hiddenCount = 0;
41277
41278
41279     // toolbar on the tabbar support...
41280     if (this.toolbar) {
41281         alert("no toolbar support yet");
41282         this.toolbar  = false;
41283         /*
41284         var tcfg = this.toolbar;
41285         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41286         this.toolbar = new Roo.Toolbar(tcfg);
41287         if (Roo.isSafari) {
41288             var tbl = tcfg.container.child('table', true);
41289             tbl.setAttribute('width', '100%');
41290         }
41291         */
41292         
41293     }
41294    
41295
41296
41297     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41298 };
41299
41300 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41301     /*
41302      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41303      */
41304     tabPosition : "top",
41305     /*
41306      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41307      */
41308     currentTabWidth : 0,
41309     /*
41310      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41311      */
41312     minTabWidth : 40,
41313     /*
41314      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41315      */
41316     maxTabWidth : 250,
41317     /*
41318      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41319      */
41320     preferredTabWidth : 175,
41321     /*
41322      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41323      */
41324     resizeTabs : false,
41325     /*
41326      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41327      */
41328     monitorResize : true,
41329     /*
41330      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41331      */
41332     toolbar : false,  // set by caller..
41333     
41334     region : false, /// set by caller
41335     
41336     disableTooltips : true, // not used yet...
41337
41338     /**
41339      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41340      * @param {String} id The id of the div to use <b>or create</b>
41341      * @param {String} text The text for the tab
41342      * @param {String} content (optional) Content to put in the TabPanelItem body
41343      * @param {Boolean} closable (optional) True to create a close icon on the tab
41344      * @return {Roo.TabPanelItem} The created TabPanelItem
41345      */
41346     addTab : function(id, text, content, closable, tpl)
41347     {
41348         var item = new Roo.bootstrap.panel.TabItem({
41349             panel: this,
41350             id : id,
41351             text : text,
41352             closable : closable,
41353             tpl : tpl
41354         });
41355         this.addTabItem(item);
41356         if(content){
41357             item.setContent(content);
41358         }
41359         return item;
41360     },
41361
41362     /**
41363      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41364      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41365      * @return {Roo.TabPanelItem}
41366      */
41367     getTab : function(id){
41368         return this.items[id];
41369     },
41370
41371     /**
41372      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41373      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41374      */
41375     hideTab : function(id){
41376         var t = this.items[id];
41377         if(!t.isHidden()){
41378            t.setHidden(true);
41379            this.hiddenCount++;
41380            this.autoSizeTabs();
41381         }
41382     },
41383
41384     /**
41385      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41386      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41387      */
41388     unhideTab : function(id){
41389         var t = this.items[id];
41390         if(t.isHidden()){
41391            t.setHidden(false);
41392            this.hiddenCount--;
41393            this.autoSizeTabs();
41394         }
41395     },
41396
41397     /**
41398      * Adds an existing {@link Roo.TabPanelItem}.
41399      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41400      */
41401     addTabItem : function(item)
41402     {
41403         this.items[item.id] = item;
41404         this.items.push(item);
41405         this.autoSizeTabs();
41406       //  if(this.resizeTabs){
41407     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41408   //         this.autoSizeTabs();
41409 //        }else{
41410 //            item.autoSize();
41411        // }
41412     },
41413
41414     /**
41415      * Removes a {@link Roo.TabPanelItem}.
41416      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41417      */
41418     removeTab : function(id){
41419         var items = this.items;
41420         var tab = items[id];
41421         if(!tab) { return; }
41422         var index = items.indexOf(tab);
41423         if(this.active == tab && items.length > 1){
41424             var newTab = this.getNextAvailable(index);
41425             if(newTab) {
41426                 newTab.activate();
41427             }
41428         }
41429         this.stripEl.dom.removeChild(tab.pnode.dom);
41430         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41431             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41432         }
41433         items.splice(index, 1);
41434         delete this.items[tab.id];
41435         tab.fireEvent("close", tab);
41436         tab.purgeListeners();
41437         this.autoSizeTabs();
41438     },
41439
41440     getNextAvailable : function(start){
41441         var items = this.items;
41442         var index = start;
41443         // look for a next tab that will slide over to
41444         // replace the one being removed
41445         while(index < items.length){
41446             var item = items[++index];
41447             if(item && !item.isHidden()){
41448                 return item;
41449             }
41450         }
41451         // if one isn't found select the previous tab (on the left)
41452         index = start;
41453         while(index >= 0){
41454             var item = items[--index];
41455             if(item && !item.isHidden()){
41456                 return item;
41457             }
41458         }
41459         return null;
41460     },
41461
41462     /**
41463      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41464      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41465      */
41466     disableTab : function(id){
41467         var tab = this.items[id];
41468         if(tab && this.active != tab){
41469             tab.disable();
41470         }
41471     },
41472
41473     /**
41474      * Enables a {@link Roo.TabPanelItem} that is disabled.
41475      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41476      */
41477     enableTab : function(id){
41478         var tab = this.items[id];
41479         tab.enable();
41480     },
41481
41482     /**
41483      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41484      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41485      * @return {Roo.TabPanelItem} The TabPanelItem.
41486      */
41487     activate : function(id)
41488     {
41489         //Roo.log('activite:'  + id);
41490         
41491         var tab = this.items[id];
41492         if(!tab){
41493             return null;
41494         }
41495         if(tab == this.active || tab.disabled){
41496             return tab;
41497         }
41498         var e = {};
41499         this.fireEvent("beforetabchange", this, e, tab);
41500         if(e.cancel !== true && !tab.disabled){
41501             if(this.active){
41502                 this.active.hide();
41503             }
41504             this.active = this.items[id];
41505             this.active.show();
41506             this.fireEvent("tabchange", this, this.active);
41507         }
41508         return tab;
41509     },
41510
41511     /**
41512      * Gets the active {@link Roo.TabPanelItem}.
41513      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41514      */
41515     getActiveTab : function(){
41516         return this.active;
41517     },
41518
41519     /**
41520      * Updates the tab body element to fit the height of the container element
41521      * for overflow scrolling
41522      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41523      */
41524     syncHeight : function(targetHeight){
41525         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41526         var bm = this.bodyEl.getMargins();
41527         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41528         this.bodyEl.setHeight(newHeight);
41529         return newHeight;
41530     },
41531
41532     onResize : function(){
41533         if(this.monitorResize){
41534             this.autoSizeTabs();
41535         }
41536     },
41537
41538     /**
41539      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41540      */
41541     beginUpdate : function(){
41542         this.updating = true;
41543     },
41544
41545     /**
41546      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41547      */
41548     endUpdate : function(){
41549         this.updating = false;
41550         this.autoSizeTabs();
41551     },
41552
41553     /**
41554      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41555      */
41556     autoSizeTabs : function()
41557     {
41558         var count = this.items.length;
41559         var vcount = count - this.hiddenCount;
41560         
41561         if (vcount < 2) {
41562             this.stripEl.hide();
41563         } else {
41564             this.stripEl.show();
41565         }
41566         
41567         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41568             return;
41569         }
41570         
41571         
41572         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41573         var availWidth = Math.floor(w / vcount);
41574         var b = this.stripBody;
41575         if(b.getWidth() > w){
41576             var tabs = this.items;
41577             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41578             if(availWidth < this.minTabWidth){
41579                 /*if(!this.sleft){    // incomplete scrolling code
41580                     this.createScrollButtons();
41581                 }
41582                 this.showScroll();
41583                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41584             }
41585         }else{
41586             if(this.currentTabWidth < this.preferredTabWidth){
41587                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41588             }
41589         }
41590     },
41591
41592     /**
41593      * Returns the number of tabs in this TabPanel.
41594      * @return {Number}
41595      */
41596      getCount : function(){
41597          return this.items.length;
41598      },
41599
41600     /**
41601      * Resizes all the tabs to the passed width
41602      * @param {Number} The new width
41603      */
41604     setTabWidth : function(width){
41605         this.currentTabWidth = width;
41606         for(var i = 0, len = this.items.length; i < len; i++) {
41607                 if(!this.items[i].isHidden()) {
41608                 this.items[i].setWidth(width);
41609             }
41610         }
41611     },
41612
41613     /**
41614      * Destroys this TabPanel
41615      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41616      */
41617     destroy : function(removeEl){
41618         Roo.EventManager.removeResizeListener(this.onResize, this);
41619         for(var i = 0, len = this.items.length; i < len; i++){
41620             this.items[i].purgeListeners();
41621         }
41622         if(removeEl === true){
41623             this.el.update("");
41624             this.el.remove();
41625         }
41626     },
41627     
41628     createStrip : function(container)
41629     {
41630         var strip = document.createElement("nav");
41631         strip.className = Roo.bootstrap.version == 4 ?
41632             "navbar-light bg-light" : 
41633             "navbar navbar-default"; //"x-tabs-wrap";
41634         container.appendChild(strip);
41635         return strip;
41636     },
41637     
41638     createStripList : function(strip)
41639     {
41640         // div wrapper for retard IE
41641         // returns the "tr" element.
41642         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41643         //'<div class="x-tabs-strip-wrap">'+
41644           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41645           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41646         return strip.firstChild; //.firstChild.firstChild.firstChild;
41647     },
41648     createBody : function(container)
41649     {
41650         var body = document.createElement("div");
41651         Roo.id(body, "tab-body");
41652         //Roo.fly(body).addClass("x-tabs-body");
41653         Roo.fly(body).addClass("tab-content");
41654         container.appendChild(body);
41655         return body;
41656     },
41657     createItemBody :function(bodyEl, id){
41658         var body = Roo.getDom(id);
41659         if(!body){
41660             body = document.createElement("div");
41661             body.id = id;
41662         }
41663         //Roo.fly(body).addClass("x-tabs-item-body");
41664         Roo.fly(body).addClass("tab-pane");
41665          bodyEl.insertBefore(body, bodyEl.firstChild);
41666         return body;
41667     },
41668     /** @private */
41669     createStripElements :  function(stripEl, text, closable, tpl)
41670     {
41671         var td = document.createElement("li"); // was td..
41672         td.className = 'nav-item';
41673         
41674         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41675         
41676         
41677         stripEl.appendChild(td);
41678         /*if(closable){
41679             td.className = "x-tabs-closable";
41680             if(!this.closeTpl){
41681                 this.closeTpl = new Roo.Template(
41682                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41683                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41684                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41685                 );
41686             }
41687             var el = this.closeTpl.overwrite(td, {"text": text});
41688             var close = el.getElementsByTagName("div")[0];
41689             var inner = el.getElementsByTagName("em")[0];
41690             return {"el": el, "close": close, "inner": inner};
41691         } else {
41692         */
41693         // not sure what this is..
41694 //            if(!this.tabTpl){
41695                 //this.tabTpl = new Roo.Template(
41696                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41697                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41698                 //);
41699 //                this.tabTpl = new Roo.Template(
41700 //                   '<a href="#">' +
41701 //                   '<span unselectable="on"' +
41702 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41703 //                            ' >{text}</span></a>'
41704 //                );
41705 //                
41706 //            }
41707
41708
41709             var template = tpl || this.tabTpl || false;
41710             
41711             if(!template){
41712                 template =  new Roo.Template(
41713                         Roo.bootstrap.version == 4 ? 
41714                             (
41715                                 '<a class="nav-link" href="#" unselectable="on"' +
41716                                      (this.disableTooltips ? '' : ' title="{text}"') +
41717                                      ' >{text}</a>'
41718                             ) : (
41719                                 '<a class="nav-link" href="#">' +
41720                                 '<span unselectable="on"' +
41721                                          (this.disableTooltips ? '' : ' title="{text}"') +
41722                                     ' >{text}</span></a>'
41723                             )
41724                 );
41725             }
41726             
41727             switch (typeof(template)) {
41728                 case 'object' :
41729                     break;
41730                 case 'string' :
41731                     template = new Roo.Template(template);
41732                     break;
41733                 default :
41734                     break;
41735             }
41736             
41737             var el = template.overwrite(td, {"text": text});
41738             
41739             var inner = el.getElementsByTagName("span")[0];
41740             
41741             return {"el": el, "inner": inner};
41742             
41743     }
41744         
41745     
41746 });
41747
41748 /**
41749  * @class Roo.TabPanelItem
41750  * @extends Roo.util.Observable
41751  * Represents an individual item (tab plus body) in a TabPanel.
41752  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41753  * @param {String} id The id of this TabPanelItem
41754  * @param {String} text The text for the tab of this TabPanelItem
41755  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41756  */
41757 Roo.bootstrap.panel.TabItem = function(config){
41758     /**
41759      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41760      * @type Roo.TabPanel
41761      */
41762     this.tabPanel = config.panel;
41763     /**
41764      * The id for this TabPanelItem
41765      * @type String
41766      */
41767     this.id = config.id;
41768     /** @private */
41769     this.disabled = false;
41770     /** @private */
41771     this.text = config.text;
41772     /** @private */
41773     this.loaded = false;
41774     this.closable = config.closable;
41775
41776     /**
41777      * The body element for this TabPanelItem.
41778      * @type Roo.Element
41779      */
41780     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41781     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41782     this.bodyEl.setStyle("display", "block");
41783     this.bodyEl.setStyle("zoom", "1");
41784     //this.hideAction();
41785
41786     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41787     /** @private */
41788     this.el = Roo.get(els.el);
41789     this.inner = Roo.get(els.inner, true);
41790      this.textEl = Roo.bootstrap.version == 4 ?
41791         this.el : Roo.get(this.el.dom.firstChild, true);
41792
41793     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41794     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41795
41796     
41797 //    this.el.on("mousedown", this.onTabMouseDown, this);
41798     this.el.on("click", this.onTabClick, this);
41799     /** @private */
41800     if(config.closable){
41801         var c = Roo.get(els.close, true);
41802         c.dom.title = this.closeText;
41803         c.addClassOnOver("close-over");
41804         c.on("click", this.closeClick, this);
41805      }
41806
41807     this.addEvents({
41808          /**
41809          * @event activate
41810          * Fires when this tab becomes the active tab.
41811          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41812          * @param {Roo.TabPanelItem} this
41813          */
41814         "activate": true,
41815         /**
41816          * @event beforeclose
41817          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41818          * @param {Roo.TabPanelItem} this
41819          * @param {Object} e Set cancel to true on this object to cancel the close.
41820          */
41821         "beforeclose": true,
41822         /**
41823          * @event close
41824          * Fires when this tab is closed.
41825          * @param {Roo.TabPanelItem} this
41826          */
41827          "close": true,
41828         /**
41829          * @event deactivate
41830          * Fires when this tab is no longer the active tab.
41831          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41832          * @param {Roo.TabPanelItem} this
41833          */
41834          "deactivate" : true
41835     });
41836     this.hidden = false;
41837
41838     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41839 };
41840
41841 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41842            {
41843     purgeListeners : function(){
41844        Roo.util.Observable.prototype.purgeListeners.call(this);
41845        this.el.removeAllListeners();
41846     },
41847     /**
41848      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41849      */
41850     show : function(){
41851         this.status_node.addClass("active");
41852         this.showAction();
41853         if(Roo.isOpera){
41854             this.tabPanel.stripWrap.repaint();
41855         }
41856         this.fireEvent("activate", this.tabPanel, this);
41857     },
41858
41859     /**
41860      * Returns true if this tab is the active tab.
41861      * @return {Boolean}
41862      */
41863     isActive : function(){
41864         return this.tabPanel.getActiveTab() == this;
41865     },
41866
41867     /**
41868      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41869      */
41870     hide : function(){
41871         this.status_node.removeClass("active");
41872         this.hideAction();
41873         this.fireEvent("deactivate", this.tabPanel, this);
41874     },
41875
41876     hideAction : function(){
41877         this.bodyEl.hide();
41878         this.bodyEl.setStyle("position", "absolute");
41879         this.bodyEl.setLeft("-20000px");
41880         this.bodyEl.setTop("-20000px");
41881     },
41882
41883     showAction : function(){
41884         this.bodyEl.setStyle("position", "relative");
41885         this.bodyEl.setTop("");
41886         this.bodyEl.setLeft("");
41887         this.bodyEl.show();
41888     },
41889
41890     /**
41891      * Set the tooltip for the tab.
41892      * @param {String} tooltip The tab's tooltip
41893      */
41894     setTooltip : function(text){
41895         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41896             this.textEl.dom.qtip = text;
41897             this.textEl.dom.removeAttribute('title');
41898         }else{
41899             this.textEl.dom.title = text;
41900         }
41901     },
41902
41903     onTabClick : function(e){
41904         e.preventDefault();
41905         this.tabPanel.activate(this.id);
41906     },
41907
41908     onTabMouseDown : function(e){
41909         e.preventDefault();
41910         this.tabPanel.activate(this.id);
41911     },
41912 /*
41913     getWidth : function(){
41914         return this.inner.getWidth();
41915     },
41916
41917     setWidth : function(width){
41918         var iwidth = width - this.linode.getPadding("lr");
41919         this.inner.setWidth(iwidth);
41920         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41921         this.linode.setWidth(width);
41922     },
41923 */
41924     /**
41925      * Show or hide the tab
41926      * @param {Boolean} hidden True to hide or false to show.
41927      */
41928     setHidden : function(hidden){
41929         this.hidden = hidden;
41930         this.linode.setStyle("display", hidden ? "none" : "");
41931     },
41932
41933     /**
41934      * Returns true if this tab is "hidden"
41935      * @return {Boolean}
41936      */
41937     isHidden : function(){
41938         return this.hidden;
41939     },
41940
41941     /**
41942      * Returns the text for this tab
41943      * @return {String}
41944      */
41945     getText : function(){
41946         return this.text;
41947     },
41948     /*
41949     autoSize : function(){
41950         //this.el.beginMeasure();
41951         this.textEl.setWidth(1);
41952         /*
41953          *  #2804 [new] Tabs in Roojs
41954          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41955          */
41956         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41957         //this.el.endMeasure();
41958     //},
41959
41960     /**
41961      * Sets the text for the tab (Note: this also sets the tooltip text)
41962      * @param {String} text The tab's text and tooltip
41963      */
41964     setText : function(text){
41965         this.text = text;
41966         this.textEl.update(text);
41967         this.setTooltip(text);
41968         //if(!this.tabPanel.resizeTabs){
41969         //    this.autoSize();
41970         //}
41971     },
41972     /**
41973      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41974      */
41975     activate : function(){
41976         this.tabPanel.activate(this.id);
41977     },
41978
41979     /**
41980      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41981      */
41982     disable : function(){
41983         if(this.tabPanel.active != this){
41984             this.disabled = true;
41985             this.status_node.addClass("disabled");
41986         }
41987     },
41988
41989     /**
41990      * Enables this TabPanelItem if it was previously disabled.
41991      */
41992     enable : function(){
41993         this.disabled = false;
41994         this.status_node.removeClass("disabled");
41995     },
41996
41997     /**
41998      * Sets the content for this TabPanelItem.
41999      * @param {String} content The content
42000      * @param {Boolean} loadScripts true to look for and load scripts
42001      */
42002     setContent : function(content, loadScripts){
42003         this.bodyEl.update(content, loadScripts);
42004     },
42005
42006     /**
42007      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42008      * @return {Roo.UpdateManager} The UpdateManager
42009      */
42010     getUpdateManager : function(){
42011         return this.bodyEl.getUpdateManager();
42012     },
42013
42014     /**
42015      * Set a URL to be used to load the content for this TabPanelItem.
42016      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42017      * @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)
42018      * @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)
42019      * @return {Roo.UpdateManager} The UpdateManager
42020      */
42021     setUrl : function(url, params, loadOnce){
42022         if(this.refreshDelegate){
42023             this.un('activate', this.refreshDelegate);
42024         }
42025         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42026         this.on("activate", this.refreshDelegate);
42027         return this.bodyEl.getUpdateManager();
42028     },
42029
42030     /** @private */
42031     _handleRefresh : function(url, params, loadOnce){
42032         if(!loadOnce || !this.loaded){
42033             var updater = this.bodyEl.getUpdateManager();
42034             updater.update(url, params, this._setLoaded.createDelegate(this));
42035         }
42036     },
42037
42038     /**
42039      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42040      *   Will fail silently if the setUrl method has not been called.
42041      *   This does not activate the panel, just updates its content.
42042      */
42043     refresh : function(){
42044         if(this.refreshDelegate){
42045            this.loaded = false;
42046            this.refreshDelegate();
42047         }
42048     },
42049
42050     /** @private */
42051     _setLoaded : function(){
42052         this.loaded = true;
42053     },
42054
42055     /** @private */
42056     closeClick : function(e){
42057         var o = {};
42058         e.stopEvent();
42059         this.fireEvent("beforeclose", this, o);
42060         if(o.cancel !== true){
42061             this.tabPanel.removeTab(this.id);
42062         }
42063     },
42064     /**
42065      * The text displayed in the tooltip for the close icon.
42066      * @type String
42067      */
42068     closeText : "Close this tab"
42069 });
42070 /**
42071 *    This script refer to:
42072 *    Title: International Telephone Input
42073 *    Author: Jack O'Connor
42074 *    Code version:  v12.1.12
42075 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42076 **/
42077
42078 Roo.bootstrap.form.PhoneInputData = function() {
42079     var d = [
42080       [
42081         "Afghanistan (‫افغانستان‬‎)",
42082         "af",
42083         "93"
42084       ],
42085       [
42086         "Albania (Shqipëri)",
42087         "al",
42088         "355"
42089       ],
42090       [
42091         "Algeria (‫الجزائر‬‎)",
42092         "dz",
42093         "213"
42094       ],
42095       [
42096         "American Samoa",
42097         "as",
42098         "1684"
42099       ],
42100       [
42101         "Andorra",
42102         "ad",
42103         "376"
42104       ],
42105       [
42106         "Angola",
42107         "ao",
42108         "244"
42109       ],
42110       [
42111         "Anguilla",
42112         "ai",
42113         "1264"
42114       ],
42115       [
42116         "Antigua and Barbuda",
42117         "ag",
42118         "1268"
42119       ],
42120       [
42121         "Argentina",
42122         "ar",
42123         "54"
42124       ],
42125       [
42126         "Armenia (Հայաստան)",
42127         "am",
42128         "374"
42129       ],
42130       [
42131         "Aruba",
42132         "aw",
42133         "297"
42134       ],
42135       [
42136         "Australia",
42137         "au",
42138         "61",
42139         0
42140       ],
42141       [
42142         "Austria (Österreich)",
42143         "at",
42144         "43"
42145       ],
42146       [
42147         "Azerbaijan (Azərbaycan)",
42148         "az",
42149         "994"
42150       ],
42151       [
42152         "Bahamas",
42153         "bs",
42154         "1242"
42155       ],
42156       [
42157         "Bahrain (‫البحرين‬‎)",
42158         "bh",
42159         "973"
42160       ],
42161       [
42162         "Bangladesh (বাংলাদেশ)",
42163         "bd",
42164         "880"
42165       ],
42166       [
42167         "Barbados",
42168         "bb",
42169         "1246"
42170       ],
42171       [
42172         "Belarus (Беларусь)",
42173         "by",
42174         "375"
42175       ],
42176       [
42177         "Belgium (België)",
42178         "be",
42179         "32"
42180       ],
42181       [
42182         "Belize",
42183         "bz",
42184         "501"
42185       ],
42186       [
42187         "Benin (Bénin)",
42188         "bj",
42189         "229"
42190       ],
42191       [
42192         "Bermuda",
42193         "bm",
42194         "1441"
42195       ],
42196       [
42197         "Bhutan (འབྲུག)",
42198         "bt",
42199         "975"
42200       ],
42201       [
42202         "Bolivia",
42203         "bo",
42204         "591"
42205       ],
42206       [
42207         "Bosnia and Herzegovina (Босна и Херцеговина)",
42208         "ba",
42209         "387"
42210       ],
42211       [
42212         "Botswana",
42213         "bw",
42214         "267"
42215       ],
42216       [
42217         "Brazil (Brasil)",
42218         "br",
42219         "55"
42220       ],
42221       [
42222         "British Indian Ocean Territory",
42223         "io",
42224         "246"
42225       ],
42226       [
42227         "British Virgin Islands",
42228         "vg",
42229         "1284"
42230       ],
42231       [
42232         "Brunei",
42233         "bn",
42234         "673"
42235       ],
42236       [
42237         "Bulgaria (България)",
42238         "bg",
42239         "359"
42240       ],
42241       [
42242         "Burkina Faso",
42243         "bf",
42244         "226"
42245       ],
42246       [
42247         "Burundi (Uburundi)",
42248         "bi",
42249         "257"
42250       ],
42251       [
42252         "Cambodia (កម្ពុជា)",
42253         "kh",
42254         "855"
42255       ],
42256       [
42257         "Cameroon (Cameroun)",
42258         "cm",
42259         "237"
42260       ],
42261       [
42262         "Canada",
42263         "ca",
42264         "1",
42265         1,
42266         ["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"]
42267       ],
42268       [
42269         "Cape Verde (Kabu Verdi)",
42270         "cv",
42271         "238"
42272       ],
42273       [
42274         "Caribbean Netherlands",
42275         "bq",
42276         "599",
42277         1
42278       ],
42279       [
42280         "Cayman Islands",
42281         "ky",
42282         "1345"
42283       ],
42284       [
42285         "Central African Republic (République centrafricaine)",
42286         "cf",
42287         "236"
42288       ],
42289       [
42290         "Chad (Tchad)",
42291         "td",
42292         "235"
42293       ],
42294       [
42295         "Chile",
42296         "cl",
42297         "56"
42298       ],
42299       [
42300         "China (中国)",
42301         "cn",
42302         "86"
42303       ],
42304       [
42305         "Christmas Island",
42306         "cx",
42307         "61",
42308         2
42309       ],
42310       [
42311         "Cocos (Keeling) Islands",
42312         "cc",
42313         "61",
42314         1
42315       ],
42316       [
42317         "Colombia",
42318         "co",
42319         "57"
42320       ],
42321       [
42322         "Comoros (‫جزر القمر‬‎)",
42323         "km",
42324         "269"
42325       ],
42326       [
42327         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42328         "cd",
42329         "243"
42330       ],
42331       [
42332         "Congo (Republic) (Congo-Brazzaville)",
42333         "cg",
42334         "242"
42335       ],
42336       [
42337         "Cook Islands",
42338         "ck",
42339         "682"
42340       ],
42341       [
42342         "Costa Rica",
42343         "cr",
42344         "506"
42345       ],
42346       [
42347         "Côte d’Ivoire",
42348         "ci",
42349         "225"
42350       ],
42351       [
42352         "Croatia (Hrvatska)",
42353         "hr",
42354         "385"
42355       ],
42356       [
42357         "Cuba",
42358         "cu",
42359         "53"
42360       ],
42361       [
42362         "Curaçao",
42363         "cw",
42364         "599",
42365         0
42366       ],
42367       [
42368         "Cyprus (Κύπρος)",
42369         "cy",
42370         "357"
42371       ],
42372       [
42373         "Czech Republic (Česká republika)",
42374         "cz",
42375         "420"
42376       ],
42377       [
42378         "Denmark (Danmark)",
42379         "dk",
42380         "45"
42381       ],
42382       [
42383         "Djibouti",
42384         "dj",
42385         "253"
42386       ],
42387       [
42388         "Dominica",
42389         "dm",
42390         "1767"
42391       ],
42392       [
42393         "Dominican Republic (República Dominicana)",
42394         "do",
42395         "1",
42396         2,
42397         ["809", "829", "849"]
42398       ],
42399       [
42400         "Ecuador",
42401         "ec",
42402         "593"
42403       ],
42404       [
42405         "Egypt (‫مصر‬‎)",
42406         "eg",
42407         "20"
42408       ],
42409       [
42410         "El Salvador",
42411         "sv",
42412         "503"
42413       ],
42414       [
42415         "Equatorial Guinea (Guinea Ecuatorial)",
42416         "gq",
42417         "240"
42418       ],
42419       [
42420         "Eritrea",
42421         "er",
42422         "291"
42423       ],
42424       [
42425         "Estonia (Eesti)",
42426         "ee",
42427         "372"
42428       ],
42429       [
42430         "Ethiopia",
42431         "et",
42432         "251"
42433       ],
42434       [
42435         "Falkland Islands (Islas Malvinas)",
42436         "fk",
42437         "500"
42438       ],
42439       [
42440         "Faroe Islands (Føroyar)",
42441         "fo",
42442         "298"
42443       ],
42444       [
42445         "Fiji",
42446         "fj",
42447         "679"
42448       ],
42449       [
42450         "Finland (Suomi)",
42451         "fi",
42452         "358",
42453         0
42454       ],
42455       [
42456         "France",
42457         "fr",
42458         "33"
42459       ],
42460       [
42461         "French Guiana (Guyane française)",
42462         "gf",
42463         "594"
42464       ],
42465       [
42466         "French Polynesia (Polynésie française)",
42467         "pf",
42468         "689"
42469       ],
42470       [
42471         "Gabon",
42472         "ga",
42473         "241"
42474       ],
42475       [
42476         "Gambia",
42477         "gm",
42478         "220"
42479       ],
42480       [
42481         "Georgia (საქართველო)",
42482         "ge",
42483         "995"
42484       ],
42485       [
42486         "Germany (Deutschland)",
42487         "de",
42488         "49"
42489       ],
42490       [
42491         "Ghana (Gaana)",
42492         "gh",
42493         "233"
42494       ],
42495       [
42496         "Gibraltar",
42497         "gi",
42498         "350"
42499       ],
42500       [
42501         "Greece (Ελλάδα)",
42502         "gr",
42503         "30"
42504       ],
42505       [
42506         "Greenland (Kalaallit Nunaat)",
42507         "gl",
42508         "299"
42509       ],
42510       [
42511         "Grenada",
42512         "gd",
42513         "1473"
42514       ],
42515       [
42516         "Guadeloupe",
42517         "gp",
42518         "590",
42519         0
42520       ],
42521       [
42522         "Guam",
42523         "gu",
42524         "1671"
42525       ],
42526       [
42527         "Guatemala",
42528         "gt",
42529         "502"
42530       ],
42531       [
42532         "Guernsey",
42533         "gg",
42534         "44",
42535         1
42536       ],
42537       [
42538         "Guinea (Guinée)",
42539         "gn",
42540         "224"
42541       ],
42542       [
42543         "Guinea-Bissau (Guiné Bissau)",
42544         "gw",
42545         "245"
42546       ],
42547       [
42548         "Guyana",
42549         "gy",
42550         "592"
42551       ],
42552       [
42553         "Haiti",
42554         "ht",
42555         "509"
42556       ],
42557       [
42558         "Honduras",
42559         "hn",
42560         "504"
42561       ],
42562       [
42563         "Hong Kong (香港)",
42564         "hk",
42565         "852"
42566       ],
42567       [
42568         "Hungary (Magyarország)",
42569         "hu",
42570         "36"
42571       ],
42572       [
42573         "Iceland (Ísland)",
42574         "is",
42575         "354"
42576       ],
42577       [
42578         "India (भारत)",
42579         "in",
42580         "91"
42581       ],
42582       [
42583         "Indonesia",
42584         "id",
42585         "62"
42586       ],
42587       [
42588         "Iran (‫ایران‬‎)",
42589         "ir",
42590         "98"
42591       ],
42592       [
42593         "Iraq (‫العراق‬‎)",
42594         "iq",
42595         "964"
42596       ],
42597       [
42598         "Ireland",
42599         "ie",
42600         "353"
42601       ],
42602       [
42603         "Isle of Man",
42604         "im",
42605         "44",
42606         2
42607       ],
42608       [
42609         "Israel (‫ישראל‬‎)",
42610         "il",
42611         "972"
42612       ],
42613       [
42614         "Italy (Italia)",
42615         "it",
42616         "39",
42617         0
42618       ],
42619       [
42620         "Jamaica",
42621         "jm",
42622         "1876"
42623       ],
42624       [
42625         "Japan (日本)",
42626         "jp",
42627         "81"
42628       ],
42629       [
42630         "Jersey",
42631         "je",
42632         "44",
42633         3
42634       ],
42635       [
42636         "Jordan (‫الأردن‬‎)",
42637         "jo",
42638         "962"
42639       ],
42640       [
42641         "Kazakhstan (Казахстан)",
42642         "kz",
42643         "7",
42644         1
42645       ],
42646       [
42647         "Kenya",
42648         "ke",
42649         "254"
42650       ],
42651       [
42652         "Kiribati",
42653         "ki",
42654         "686"
42655       ],
42656       [
42657         "Kosovo",
42658         "xk",
42659         "383"
42660       ],
42661       [
42662         "Kuwait (‫الكويت‬‎)",
42663         "kw",
42664         "965"
42665       ],
42666       [
42667         "Kyrgyzstan (Кыргызстан)",
42668         "kg",
42669         "996"
42670       ],
42671       [
42672         "Laos (ລາວ)",
42673         "la",
42674         "856"
42675       ],
42676       [
42677         "Latvia (Latvija)",
42678         "lv",
42679         "371"
42680       ],
42681       [
42682         "Lebanon (‫لبنان‬‎)",
42683         "lb",
42684         "961"
42685       ],
42686       [
42687         "Lesotho",
42688         "ls",
42689         "266"
42690       ],
42691       [
42692         "Liberia",
42693         "lr",
42694         "231"
42695       ],
42696       [
42697         "Libya (‫ليبيا‬‎)",
42698         "ly",
42699         "218"
42700       ],
42701       [
42702         "Liechtenstein",
42703         "li",
42704         "423"
42705       ],
42706       [
42707         "Lithuania (Lietuva)",
42708         "lt",
42709         "370"
42710       ],
42711       [
42712         "Luxembourg",
42713         "lu",
42714         "352"
42715       ],
42716       [
42717         "Macau (澳門)",
42718         "mo",
42719         "853"
42720       ],
42721       [
42722         "Macedonia (FYROM) (Македонија)",
42723         "mk",
42724         "389"
42725       ],
42726       [
42727         "Madagascar (Madagasikara)",
42728         "mg",
42729         "261"
42730       ],
42731       [
42732         "Malawi",
42733         "mw",
42734         "265"
42735       ],
42736       [
42737         "Malaysia",
42738         "my",
42739         "60"
42740       ],
42741       [
42742         "Maldives",
42743         "mv",
42744         "960"
42745       ],
42746       [
42747         "Mali",
42748         "ml",
42749         "223"
42750       ],
42751       [
42752         "Malta",
42753         "mt",
42754         "356"
42755       ],
42756       [
42757         "Marshall Islands",
42758         "mh",
42759         "692"
42760       ],
42761       [
42762         "Martinique",
42763         "mq",
42764         "596"
42765       ],
42766       [
42767         "Mauritania (‫موريتانيا‬‎)",
42768         "mr",
42769         "222"
42770       ],
42771       [
42772         "Mauritius (Moris)",
42773         "mu",
42774         "230"
42775       ],
42776       [
42777         "Mayotte",
42778         "yt",
42779         "262",
42780         1
42781       ],
42782       [
42783         "Mexico (México)",
42784         "mx",
42785         "52"
42786       ],
42787       [
42788         "Micronesia",
42789         "fm",
42790         "691"
42791       ],
42792       [
42793         "Moldova (Republica Moldova)",
42794         "md",
42795         "373"
42796       ],
42797       [
42798         "Monaco",
42799         "mc",
42800         "377"
42801       ],
42802       [
42803         "Mongolia (Монгол)",
42804         "mn",
42805         "976"
42806       ],
42807       [
42808         "Montenegro (Crna Gora)",
42809         "me",
42810         "382"
42811       ],
42812       [
42813         "Montserrat",
42814         "ms",
42815         "1664"
42816       ],
42817       [
42818         "Morocco (‫المغرب‬‎)",
42819         "ma",
42820         "212",
42821         0
42822       ],
42823       [
42824         "Mozambique (Moçambique)",
42825         "mz",
42826         "258"
42827       ],
42828       [
42829         "Myanmar (Burma) (မြန်မာ)",
42830         "mm",
42831         "95"
42832       ],
42833       [
42834         "Namibia (Namibië)",
42835         "na",
42836         "264"
42837       ],
42838       [
42839         "Nauru",
42840         "nr",
42841         "674"
42842       ],
42843       [
42844         "Nepal (नेपाल)",
42845         "np",
42846         "977"
42847       ],
42848       [
42849         "Netherlands (Nederland)",
42850         "nl",
42851         "31"
42852       ],
42853       [
42854         "New Caledonia (Nouvelle-Calédonie)",
42855         "nc",
42856         "687"
42857       ],
42858       [
42859         "New Zealand",
42860         "nz",
42861         "64"
42862       ],
42863       [
42864         "Nicaragua",
42865         "ni",
42866         "505"
42867       ],
42868       [
42869         "Niger (Nijar)",
42870         "ne",
42871         "227"
42872       ],
42873       [
42874         "Nigeria",
42875         "ng",
42876         "234"
42877       ],
42878       [
42879         "Niue",
42880         "nu",
42881         "683"
42882       ],
42883       [
42884         "Norfolk Island",
42885         "nf",
42886         "672"
42887       ],
42888       [
42889         "North Korea (조선 민주주의 인민 공화국)",
42890         "kp",
42891         "850"
42892       ],
42893       [
42894         "Northern Mariana Islands",
42895         "mp",
42896         "1670"
42897       ],
42898       [
42899         "Norway (Norge)",
42900         "no",
42901         "47",
42902         0
42903       ],
42904       [
42905         "Oman (‫عُمان‬‎)",
42906         "om",
42907         "968"
42908       ],
42909       [
42910         "Pakistan (‫پاکستان‬‎)",
42911         "pk",
42912         "92"
42913       ],
42914       [
42915         "Palau",
42916         "pw",
42917         "680"
42918       ],
42919       [
42920         "Palestine (‫فلسطين‬‎)",
42921         "ps",
42922         "970"
42923       ],
42924       [
42925         "Panama (Panamá)",
42926         "pa",
42927         "507"
42928       ],
42929       [
42930         "Papua New Guinea",
42931         "pg",
42932         "675"
42933       ],
42934       [
42935         "Paraguay",
42936         "py",
42937         "595"
42938       ],
42939       [
42940         "Peru (Perú)",
42941         "pe",
42942         "51"
42943       ],
42944       [
42945         "Philippines",
42946         "ph",
42947         "63"
42948       ],
42949       [
42950         "Poland (Polska)",
42951         "pl",
42952         "48"
42953       ],
42954       [
42955         "Portugal",
42956         "pt",
42957         "351"
42958       ],
42959       [
42960         "Puerto Rico",
42961         "pr",
42962         "1",
42963         3,
42964         ["787", "939"]
42965       ],
42966       [
42967         "Qatar (‫قطر‬‎)",
42968         "qa",
42969         "974"
42970       ],
42971       [
42972         "Réunion (La Réunion)",
42973         "re",
42974         "262",
42975         0
42976       ],
42977       [
42978         "Romania (România)",
42979         "ro",
42980         "40"
42981       ],
42982       [
42983         "Russia (Россия)",
42984         "ru",
42985         "7",
42986         0
42987       ],
42988       [
42989         "Rwanda",
42990         "rw",
42991         "250"
42992       ],
42993       [
42994         "Saint Barthélemy",
42995         "bl",
42996         "590",
42997         1
42998       ],
42999       [
43000         "Saint Helena",
43001         "sh",
43002         "290"
43003       ],
43004       [
43005         "Saint Kitts and Nevis",
43006         "kn",
43007         "1869"
43008       ],
43009       [
43010         "Saint Lucia",
43011         "lc",
43012         "1758"
43013       ],
43014       [
43015         "Saint Martin (Saint-Martin (partie française))",
43016         "mf",
43017         "590",
43018         2
43019       ],
43020       [
43021         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43022         "pm",
43023         "508"
43024       ],
43025       [
43026         "Saint Vincent and the Grenadines",
43027         "vc",
43028         "1784"
43029       ],
43030       [
43031         "Samoa",
43032         "ws",
43033         "685"
43034       ],
43035       [
43036         "San Marino",
43037         "sm",
43038         "378"
43039       ],
43040       [
43041         "São Tomé and Príncipe (São Tomé e Príncipe)",
43042         "st",
43043         "239"
43044       ],
43045       [
43046         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43047         "sa",
43048         "966"
43049       ],
43050       [
43051         "Senegal (Sénégal)",
43052         "sn",
43053         "221"
43054       ],
43055       [
43056         "Serbia (Србија)",
43057         "rs",
43058         "381"
43059       ],
43060       [
43061         "Seychelles",
43062         "sc",
43063         "248"
43064       ],
43065       [
43066         "Sierra Leone",
43067         "sl",
43068         "232"
43069       ],
43070       [
43071         "Singapore",
43072         "sg",
43073         "65"
43074       ],
43075       [
43076         "Sint Maarten",
43077         "sx",
43078         "1721"
43079       ],
43080       [
43081         "Slovakia (Slovensko)",
43082         "sk",
43083         "421"
43084       ],
43085       [
43086         "Slovenia (Slovenija)",
43087         "si",
43088         "386"
43089       ],
43090       [
43091         "Solomon Islands",
43092         "sb",
43093         "677"
43094       ],
43095       [
43096         "Somalia (Soomaaliya)",
43097         "so",
43098         "252"
43099       ],
43100       [
43101         "South Africa",
43102         "za",
43103         "27"
43104       ],
43105       [
43106         "South Korea (대한민국)",
43107         "kr",
43108         "82"
43109       ],
43110       [
43111         "South Sudan (‫جنوب السودان‬‎)",
43112         "ss",
43113         "211"
43114       ],
43115       [
43116         "Spain (España)",
43117         "es",
43118         "34"
43119       ],
43120       [
43121         "Sri Lanka (ශ්‍රී ලංකාව)",
43122         "lk",
43123         "94"
43124       ],
43125       [
43126         "Sudan (‫السودان‬‎)",
43127         "sd",
43128         "249"
43129       ],
43130       [
43131         "Suriname",
43132         "sr",
43133         "597"
43134       ],
43135       [
43136         "Svalbard and Jan Mayen",
43137         "sj",
43138         "47",
43139         1
43140       ],
43141       [
43142         "Swaziland",
43143         "sz",
43144         "268"
43145       ],
43146       [
43147         "Sweden (Sverige)",
43148         "se",
43149         "46"
43150       ],
43151       [
43152         "Switzerland (Schweiz)",
43153         "ch",
43154         "41"
43155       ],
43156       [
43157         "Syria (‫سوريا‬‎)",
43158         "sy",
43159         "963"
43160       ],
43161       [
43162         "Taiwan (台灣)",
43163         "tw",
43164         "886"
43165       ],
43166       [
43167         "Tajikistan",
43168         "tj",
43169         "992"
43170       ],
43171       [
43172         "Tanzania",
43173         "tz",
43174         "255"
43175       ],
43176       [
43177         "Thailand (ไทย)",
43178         "th",
43179         "66"
43180       ],
43181       [
43182         "Timor-Leste",
43183         "tl",
43184         "670"
43185       ],
43186       [
43187         "Togo",
43188         "tg",
43189         "228"
43190       ],
43191       [
43192         "Tokelau",
43193         "tk",
43194         "690"
43195       ],
43196       [
43197         "Tonga",
43198         "to",
43199         "676"
43200       ],
43201       [
43202         "Trinidad and Tobago",
43203         "tt",
43204         "1868"
43205       ],
43206       [
43207         "Tunisia (‫تونس‬‎)",
43208         "tn",
43209         "216"
43210       ],
43211       [
43212         "Turkey (Türkiye)",
43213         "tr",
43214         "90"
43215       ],
43216       [
43217         "Turkmenistan",
43218         "tm",
43219         "993"
43220       ],
43221       [
43222         "Turks and Caicos Islands",
43223         "tc",
43224         "1649"
43225       ],
43226       [
43227         "Tuvalu",
43228         "tv",
43229         "688"
43230       ],
43231       [
43232         "U.S. Virgin Islands",
43233         "vi",
43234         "1340"
43235       ],
43236       [
43237         "Uganda",
43238         "ug",
43239         "256"
43240       ],
43241       [
43242         "Ukraine (Україна)",
43243         "ua",
43244         "380"
43245       ],
43246       [
43247         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43248         "ae",
43249         "971"
43250       ],
43251       [
43252         "United Kingdom",
43253         "gb",
43254         "44",
43255         0
43256       ],
43257       [
43258         "United States",
43259         "us",
43260         "1",
43261         0
43262       ],
43263       [
43264         "Uruguay",
43265         "uy",
43266         "598"
43267       ],
43268       [
43269         "Uzbekistan (Oʻzbekiston)",
43270         "uz",
43271         "998"
43272       ],
43273       [
43274         "Vanuatu",
43275         "vu",
43276         "678"
43277       ],
43278       [
43279         "Vatican City (Città del Vaticano)",
43280         "va",
43281         "39",
43282         1
43283       ],
43284       [
43285         "Venezuela",
43286         "ve",
43287         "58"
43288       ],
43289       [
43290         "Vietnam (Việt Nam)",
43291         "vn",
43292         "84"
43293       ],
43294       [
43295         "Wallis and Futuna (Wallis-et-Futuna)",
43296         "wf",
43297         "681"
43298       ],
43299       [
43300         "Western Sahara (‫الصحراء الغربية‬‎)",
43301         "eh",
43302         "212",
43303         1
43304       ],
43305       [
43306         "Yemen (‫اليمن‬‎)",
43307         "ye",
43308         "967"
43309       ],
43310       [
43311         "Zambia",
43312         "zm",
43313         "260"
43314       ],
43315       [
43316         "Zimbabwe",
43317         "zw",
43318         "263"
43319       ],
43320       [
43321         "Åland Islands",
43322         "ax",
43323         "358",
43324         1
43325       ]
43326   ];
43327   
43328   return d;
43329 }/**
43330 *    This script refer to:
43331 *    Title: International Telephone Input
43332 *    Author: Jack O'Connor
43333 *    Code version:  v12.1.12
43334 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43335 **/
43336
43337 /**
43338  * @class Roo.bootstrap.form.PhoneInput
43339  * @extends Roo.bootstrap.form.TriggerField
43340  * An input with International dial-code selection
43341  
43342  * @cfg {String} defaultDialCode default '+852'
43343  * @cfg {Array} preferedCountries default []
43344   
43345  * @constructor
43346  * Create a new PhoneInput.
43347  * @param {Object} config Configuration options
43348  */
43349
43350 Roo.bootstrap.form.PhoneInput = function(config) {
43351     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43352 };
43353
43354 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43355         /**
43356         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43357         */
43358         listWidth: undefined,
43359         
43360         selectedClass: 'active',
43361         
43362         invalidClass : "has-warning",
43363         
43364         validClass: 'has-success',
43365         
43366         allowed: '0123456789',
43367         
43368         max_length: 15,
43369         
43370         /**
43371          * @cfg {String} defaultDialCode The default dial code when initializing the input
43372          */
43373         defaultDialCode: '+852',
43374         
43375         /**
43376          * @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
43377          */
43378         preferedCountries: false,
43379         
43380         getAutoCreate : function()
43381         {
43382             var data = Roo.bootstrap.form.PhoneInputData();
43383             var align = this.labelAlign || this.parentLabelAlign();
43384             var id = Roo.id();
43385             
43386             this.allCountries = [];
43387             this.dialCodeMapping = [];
43388             
43389             for (var i = 0; i < data.length; i++) {
43390               var c = data[i];
43391               this.allCountries[i] = {
43392                 name: c[0],
43393                 iso2: c[1],
43394                 dialCode: c[2],
43395                 priority: c[3] || 0,
43396                 areaCodes: c[4] || null
43397               };
43398               this.dialCodeMapping[c[2]] = {
43399                   name: c[0],
43400                   iso2: c[1],
43401                   priority: c[3] || 0,
43402                   areaCodes: c[4] || null
43403               };
43404             }
43405             
43406             var cfg = {
43407                 cls: 'form-group',
43408                 cn: []
43409             };
43410             
43411             var input =  {
43412                 tag: 'input',
43413                 id : id,
43414                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43415                 maxlength: this.max_length,
43416                 cls : 'form-control tel-input',
43417                 autocomplete: 'new-password'
43418             };
43419             
43420             var hiddenInput = {
43421                 tag: 'input',
43422                 type: 'hidden',
43423                 cls: 'hidden-tel-input'
43424             };
43425             
43426             if (this.name) {
43427                 hiddenInput.name = this.name;
43428             }
43429             
43430             if (this.disabled) {
43431                 input.disabled = true;
43432             }
43433             
43434             var flag_container = {
43435                 tag: 'div',
43436                 cls: 'flag-box',
43437                 cn: [
43438                     {
43439                         tag: 'div',
43440                         cls: 'flag'
43441                     },
43442                     {
43443                         tag: 'div',
43444                         cls: 'caret'
43445                     }
43446                 ]
43447             };
43448             
43449             var box = {
43450                 tag: 'div',
43451                 cls: this.hasFeedback ? 'has-feedback' : '',
43452                 cn: [
43453                     hiddenInput,
43454                     input,
43455                     {
43456                         tag: 'input',
43457                         cls: 'dial-code-holder',
43458                         disabled: true
43459                     }
43460                 ]
43461             };
43462             
43463             var container = {
43464                 cls: 'roo-select2-container input-group',
43465                 cn: [
43466                     flag_container,
43467                     box
43468                 ]
43469             };
43470             
43471             if (this.fieldLabel.length) {
43472                 var indicator = {
43473                     tag: 'i',
43474                     tooltip: 'This field is required'
43475                 };
43476                 
43477                 var label = {
43478                     tag: 'label',
43479                     'for':  id,
43480                     cls: 'control-label',
43481                     cn: []
43482                 };
43483                 
43484                 var label_text = {
43485                     tag: 'span',
43486                     html: this.fieldLabel
43487                 };
43488                 
43489                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43490                 label.cn = [
43491                     indicator,
43492                     label_text
43493                 ];
43494                 
43495                 if(this.indicatorpos == 'right') {
43496                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43497                     label.cn = [
43498                         label_text,
43499                         indicator
43500                     ];
43501                 }
43502                 
43503                 if(align == 'left') {
43504                     container = {
43505                         tag: 'div',
43506                         cn: [
43507                             container
43508                         ]
43509                     };
43510                     
43511                     if(this.labelWidth > 12){
43512                         label.style = "width: " + this.labelWidth + 'px';
43513                     }
43514                     if(this.labelWidth < 13 && this.labelmd == 0){
43515                         this.labelmd = this.labelWidth;
43516                     }
43517                     if(this.labellg > 0){
43518                         label.cls += ' col-lg-' + this.labellg;
43519                         input.cls += ' col-lg-' + (12 - this.labellg);
43520                     }
43521                     if(this.labelmd > 0){
43522                         label.cls += ' col-md-' + this.labelmd;
43523                         container.cls += ' col-md-' + (12 - this.labelmd);
43524                     }
43525                     if(this.labelsm > 0){
43526                         label.cls += ' col-sm-' + this.labelsm;
43527                         container.cls += ' col-sm-' + (12 - this.labelsm);
43528                     }
43529                     if(this.labelxs > 0){
43530                         label.cls += ' col-xs-' + this.labelxs;
43531                         container.cls += ' col-xs-' + (12 - this.labelxs);
43532                     }
43533                 }
43534             }
43535             
43536             cfg.cn = [
43537                 label,
43538                 container
43539             ];
43540             
43541             var settings = this;
43542             
43543             ['xs','sm','md','lg'].map(function(size){
43544                 if (settings[size]) {
43545                     cfg.cls += ' col-' + size + '-' + settings[size];
43546                 }
43547             });
43548             
43549             this.store = new Roo.data.Store({
43550                 proxy : new Roo.data.MemoryProxy({}),
43551                 reader : new Roo.data.JsonReader({
43552                     fields : [
43553                         {
43554                             'name' : 'name',
43555                             'type' : 'string'
43556                         },
43557                         {
43558                             'name' : 'iso2',
43559                             'type' : 'string'
43560                         },
43561                         {
43562                             'name' : 'dialCode',
43563                             'type' : 'string'
43564                         },
43565                         {
43566                             'name' : 'priority',
43567                             'type' : 'string'
43568                         },
43569                         {
43570                             'name' : 'areaCodes',
43571                             'type' : 'string'
43572                         }
43573                     ]
43574                 })
43575             });
43576             
43577             if(!this.preferedCountries) {
43578                 this.preferedCountries = [
43579                     'hk',
43580                     'gb',
43581                     'us'
43582                 ];
43583             }
43584             
43585             var p = this.preferedCountries.reverse();
43586             
43587             if(p) {
43588                 for (var i = 0; i < p.length; i++) {
43589                     for (var j = 0; j < this.allCountries.length; j++) {
43590                         if(this.allCountries[j].iso2 == p[i]) {
43591                             var t = this.allCountries[j];
43592                             this.allCountries.splice(j,1);
43593                             this.allCountries.unshift(t);
43594                         }
43595                     } 
43596                 }
43597             }
43598             
43599             this.store.proxy.data = {
43600                 success: true,
43601                 data: this.allCountries
43602             };
43603             
43604             return cfg;
43605         },
43606         
43607         initEvents : function()
43608         {
43609             this.createList();
43610             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43611             
43612             this.indicator = this.indicatorEl();
43613             this.flag = this.flagEl();
43614             this.dialCodeHolder = this.dialCodeHolderEl();
43615             
43616             this.trigger = this.el.select('div.flag-box',true).first();
43617             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43618             
43619             var _this = this;
43620             
43621             (function(){
43622                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43623                 _this.list.setWidth(lw);
43624             }).defer(100);
43625             
43626             this.list.on('mouseover', this.onViewOver, this);
43627             this.list.on('mousemove', this.onViewMove, this);
43628             this.inputEl().on("keyup", this.onKeyUp, this);
43629             this.inputEl().on("keypress", this.onKeyPress, this);
43630             
43631             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43632
43633             this.view = new Roo.View(this.list, this.tpl, {
43634                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43635             });
43636             
43637             this.view.on('click', this.onViewClick, this);
43638             this.setValue(this.defaultDialCode);
43639         },
43640         
43641         onTriggerClick : function(e)
43642         {
43643             Roo.log('trigger click');
43644             if(this.disabled){
43645                 return;
43646             }
43647             
43648             if(this.isExpanded()){
43649                 this.collapse();
43650                 this.hasFocus = false;
43651             }else {
43652                 this.store.load({});
43653                 this.hasFocus = true;
43654                 this.expand();
43655             }
43656         },
43657         
43658         isExpanded : function()
43659         {
43660             return this.list.isVisible();
43661         },
43662         
43663         collapse : function()
43664         {
43665             if(!this.isExpanded()){
43666                 return;
43667             }
43668             this.list.hide();
43669             Roo.get(document).un('mousedown', this.collapseIf, this);
43670             Roo.get(document).un('mousewheel', this.collapseIf, this);
43671             this.fireEvent('collapse', this);
43672             this.validate();
43673         },
43674         
43675         expand : function()
43676         {
43677             Roo.log('expand');
43678
43679             if(this.isExpanded() || !this.hasFocus){
43680                 return;
43681             }
43682             
43683             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43684             this.list.setWidth(lw);
43685             
43686             this.list.show();
43687             this.restrictHeight();
43688             
43689             Roo.get(document).on('mousedown', this.collapseIf, this);
43690             Roo.get(document).on('mousewheel', this.collapseIf, this);
43691             
43692             this.fireEvent('expand', this);
43693         },
43694         
43695         restrictHeight : function()
43696         {
43697             this.list.alignTo(this.inputEl(), this.listAlign);
43698             this.list.alignTo(this.inputEl(), this.listAlign);
43699         },
43700         
43701         onViewOver : function(e, t)
43702         {
43703             if(this.inKeyMode){
43704                 return;
43705             }
43706             var item = this.view.findItemFromChild(t);
43707             
43708             if(item){
43709                 var index = this.view.indexOf(item);
43710                 this.select(index, false);
43711             }
43712         },
43713
43714         // private
43715         onViewClick : function(view, doFocus, el, e)
43716         {
43717             var index = this.view.getSelectedIndexes()[0];
43718             
43719             var r = this.store.getAt(index);
43720             
43721             if(r){
43722                 this.onSelect(r, index);
43723             }
43724             if(doFocus !== false && !this.blockFocus){
43725                 this.inputEl().focus();
43726             }
43727         },
43728         
43729         onViewMove : function(e, t)
43730         {
43731             this.inKeyMode = false;
43732         },
43733         
43734         select : function(index, scrollIntoView)
43735         {
43736             this.selectedIndex = index;
43737             this.view.select(index);
43738             if(scrollIntoView !== false){
43739                 var el = this.view.getNode(index);
43740                 if(el){
43741                     this.list.scrollChildIntoView(el, false);
43742                 }
43743             }
43744         },
43745         
43746         createList : function()
43747         {
43748             this.list = Roo.get(document.body).createChild({
43749                 tag: 'ul',
43750                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43751                 style: 'display:none'
43752             });
43753             
43754             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43755         },
43756         
43757         collapseIf : function(e)
43758         {
43759             var in_combo  = e.within(this.el);
43760             var in_list =  e.within(this.list);
43761             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43762             
43763             if (in_combo || in_list || is_list) {
43764                 return;
43765             }
43766             this.collapse();
43767         },
43768         
43769         onSelect : function(record, index)
43770         {
43771             if(this.fireEvent('beforeselect', this, record, index) !== false){
43772                 
43773                 this.setFlagClass(record.data.iso2);
43774                 this.setDialCode(record.data.dialCode);
43775                 this.hasFocus = false;
43776                 this.collapse();
43777                 this.fireEvent('select', this, record, index);
43778             }
43779         },
43780         
43781         flagEl : function()
43782         {
43783             var flag = this.el.select('div.flag',true).first();
43784             if(!flag){
43785                 return false;
43786             }
43787             return flag;
43788         },
43789         
43790         dialCodeHolderEl : function()
43791         {
43792             var d = this.el.select('input.dial-code-holder',true).first();
43793             if(!d){
43794                 return false;
43795             }
43796             return d;
43797         },
43798         
43799         setDialCode : function(v)
43800         {
43801             this.dialCodeHolder.dom.value = '+'+v;
43802         },
43803         
43804         setFlagClass : function(n)
43805         {
43806             this.flag.dom.className = 'flag '+n;
43807         },
43808         
43809         getValue : function()
43810         {
43811             var v = this.inputEl().getValue();
43812             if(this.dialCodeHolder) {
43813                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43814             }
43815             return v;
43816         },
43817         
43818         setValue : function(v)
43819         {
43820             var d = this.getDialCode(v);
43821             
43822             //invalid dial code
43823             if(v.length == 0 || !d || d.length == 0) {
43824                 if(this.rendered){
43825                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43826                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43827                 }
43828                 return;
43829             }
43830             
43831             //valid dial code
43832             this.setFlagClass(this.dialCodeMapping[d].iso2);
43833             this.setDialCode(d);
43834             this.inputEl().dom.value = v.replace('+'+d,'');
43835             this.hiddenEl().dom.value = this.getValue();
43836             
43837             this.validate();
43838         },
43839         
43840         getDialCode : function(v)
43841         {
43842             v = v ||  '';
43843             
43844             if (v.length == 0) {
43845                 return this.dialCodeHolder.dom.value;
43846             }
43847             
43848             var dialCode = "";
43849             if (v.charAt(0) != "+") {
43850                 return false;
43851             }
43852             var numericChars = "";
43853             for (var i = 1; i < v.length; i++) {
43854               var c = v.charAt(i);
43855               if (!isNaN(c)) {
43856                 numericChars += c;
43857                 if (this.dialCodeMapping[numericChars]) {
43858                   dialCode = v.substr(1, i);
43859                 }
43860                 if (numericChars.length == 4) {
43861                   break;
43862                 }
43863               }
43864             }
43865             return dialCode;
43866         },
43867         
43868         reset : function()
43869         {
43870             this.setValue(this.defaultDialCode);
43871             this.validate();
43872         },
43873         
43874         hiddenEl : function()
43875         {
43876             return this.el.select('input.hidden-tel-input',true).first();
43877         },
43878         
43879         // after setting val
43880         onKeyUp : function(e){
43881             this.setValue(this.getValue());
43882         },
43883         
43884         onKeyPress : function(e){
43885             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43886                 e.stopEvent();
43887             }
43888         }
43889         
43890 });
43891 /**
43892  * @class Roo.bootstrap.form.MoneyField
43893  * @extends Roo.bootstrap.form.ComboBox
43894  * Bootstrap MoneyField class
43895  * 
43896  * @constructor
43897  * Create a new MoneyField.
43898  * @param {Object} config Configuration options
43899  */
43900
43901 Roo.bootstrap.form.MoneyField = function(config) {
43902     
43903     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43904     
43905 };
43906
43907 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43908     
43909     /**
43910      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43911      */
43912     allowDecimals : true,
43913     /**
43914      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43915      */
43916     decimalSeparator : ".",
43917     /**
43918      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43919      */
43920     decimalPrecision : 0,
43921     /**
43922      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43923      */
43924     allowNegative : true,
43925     /**
43926      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43927      */
43928     allowZero: true,
43929     /**
43930      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43931      */
43932     minValue : Number.NEGATIVE_INFINITY,
43933     /**
43934      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43935      */
43936     maxValue : Number.MAX_VALUE,
43937     /**
43938      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43939      */
43940     minText : "The minimum value for this field is {0}",
43941     /**
43942      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43943      */
43944     maxText : "The maximum value for this field is {0}",
43945     /**
43946      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43947      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43948      */
43949     nanText : "{0} is not a valid number",
43950     /**
43951      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43952      */
43953     castInt : true,
43954     /**
43955      * @cfg {String} defaults currency of the MoneyField
43956      * value should be in lkey
43957      */
43958     defaultCurrency : false,
43959     /**
43960      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43961      */
43962     thousandsDelimiter : false,
43963     /**
43964      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43965      */
43966     max_length: false,
43967     
43968     inputlg : 9,
43969     inputmd : 9,
43970     inputsm : 9,
43971     inputxs : 6,
43972      /**
43973      * @cfg {Roo.data.Store} store  Store to lookup currency??
43974      */
43975     store : false,
43976     
43977     getAutoCreate : function()
43978     {
43979         var align = this.labelAlign || this.parentLabelAlign();
43980         
43981         var id = Roo.id();
43982
43983         var cfg = {
43984             cls: 'form-group',
43985             cn: []
43986         };
43987
43988         var input =  {
43989             tag: 'input',
43990             id : id,
43991             cls : 'form-control roo-money-amount-input',
43992             autocomplete: 'new-password'
43993         };
43994         
43995         var hiddenInput = {
43996             tag: 'input',
43997             type: 'hidden',
43998             id: Roo.id(),
43999             cls: 'hidden-number-input'
44000         };
44001         
44002         if(this.max_length) {
44003             input.maxlength = this.max_length; 
44004         }
44005         
44006         if (this.name) {
44007             hiddenInput.name = this.name;
44008         }
44009
44010         if (this.disabled) {
44011             input.disabled = true;
44012         }
44013
44014         var clg = 12 - this.inputlg;
44015         var cmd = 12 - this.inputmd;
44016         var csm = 12 - this.inputsm;
44017         var cxs = 12 - this.inputxs;
44018         
44019         var container = {
44020             tag : 'div',
44021             cls : 'row roo-money-field',
44022             cn : [
44023                 {
44024                     tag : 'div',
44025                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44026                     cn : [
44027                         {
44028                             tag : 'div',
44029                             cls: 'roo-select2-container input-group',
44030                             cn: [
44031                                 {
44032                                     tag : 'input',
44033                                     cls : 'form-control roo-money-currency-input',
44034                                     autocomplete: 'new-password',
44035                                     readOnly : 1,
44036                                     name : this.currencyName
44037                                 },
44038                                 {
44039                                     tag :'span',
44040                                     cls : 'input-group-addon',
44041                                     cn : [
44042                                         {
44043                                             tag: 'span',
44044                                             cls: 'caret'
44045                                         }
44046                                     ]
44047                                 }
44048                             ]
44049                         }
44050                     ]
44051                 },
44052                 {
44053                     tag : 'div',
44054                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44055                     cn : [
44056                         {
44057                             tag: 'div',
44058                             cls: this.hasFeedback ? 'has-feedback' : '',
44059                             cn: [
44060                                 input
44061                             ]
44062                         }
44063                     ]
44064                 }
44065             ]
44066             
44067         };
44068         
44069         if (this.fieldLabel.length) {
44070             var indicator = {
44071                 tag: 'i',
44072                 tooltip: 'This field is required'
44073             };
44074
44075             var label = {
44076                 tag: 'label',
44077                 'for':  id,
44078                 cls: 'control-label',
44079                 cn: []
44080             };
44081
44082             var label_text = {
44083                 tag: 'span',
44084                 html: this.fieldLabel
44085             };
44086
44087             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44088             label.cn = [
44089                 indicator,
44090                 label_text
44091             ];
44092
44093             if(this.indicatorpos == 'right') {
44094                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44095                 label.cn = [
44096                     label_text,
44097                     indicator
44098                 ];
44099             }
44100
44101             if(align == 'left') {
44102                 container = {
44103                     tag: 'div',
44104                     cn: [
44105                         container
44106                     ]
44107                 };
44108
44109                 if(this.labelWidth > 12){
44110                     label.style = "width: " + this.labelWidth + 'px';
44111                 }
44112                 if(this.labelWidth < 13 && this.labelmd == 0){
44113                     this.labelmd = this.labelWidth;
44114                 }
44115                 if(this.labellg > 0){
44116                     label.cls += ' col-lg-' + this.labellg;
44117                     input.cls += ' col-lg-' + (12 - this.labellg);
44118                 }
44119                 if(this.labelmd > 0){
44120                     label.cls += ' col-md-' + this.labelmd;
44121                     container.cls += ' col-md-' + (12 - this.labelmd);
44122                 }
44123                 if(this.labelsm > 0){
44124                     label.cls += ' col-sm-' + this.labelsm;
44125                     container.cls += ' col-sm-' + (12 - this.labelsm);
44126                 }
44127                 if(this.labelxs > 0){
44128                     label.cls += ' col-xs-' + this.labelxs;
44129                     container.cls += ' col-xs-' + (12 - this.labelxs);
44130                 }
44131             }
44132         }
44133
44134         cfg.cn = [
44135             label,
44136             container,
44137             hiddenInput
44138         ];
44139         
44140         var settings = this;
44141
44142         ['xs','sm','md','lg'].map(function(size){
44143             if (settings[size]) {
44144                 cfg.cls += ' col-' + size + '-' + settings[size];
44145             }
44146         });
44147         
44148         return cfg;
44149     },
44150     
44151     initEvents : function()
44152     {
44153         this.indicator = this.indicatorEl();
44154         
44155         this.initCurrencyEvent();
44156         
44157         this.initNumberEvent();
44158     },
44159     
44160     initCurrencyEvent : function()
44161     {
44162         if (!this.store) {
44163             throw "can not find store for combo";
44164         }
44165         
44166         this.store = Roo.factory(this.store, Roo.data);
44167         this.store.parent = this;
44168         
44169         this.createList();
44170         
44171         this.triggerEl = this.el.select('.input-group-addon', true).first();
44172         
44173         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44174         
44175         var _this = this;
44176         
44177         (function(){
44178             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44179             _this.list.setWidth(lw);
44180         }).defer(100);
44181         
44182         this.list.on('mouseover', this.onViewOver, this);
44183         this.list.on('mousemove', this.onViewMove, this);
44184         this.list.on('scroll', this.onViewScroll, this);
44185         
44186         if(!this.tpl){
44187             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44188         }
44189         
44190         this.view = new Roo.View(this.list, this.tpl, {
44191             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44192         });
44193         
44194         this.view.on('click', this.onViewClick, this);
44195         
44196         this.store.on('beforeload', this.onBeforeLoad, this);
44197         this.store.on('load', this.onLoad, this);
44198         this.store.on('loadexception', this.onLoadException, this);
44199         
44200         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44201             "up" : function(e){
44202                 this.inKeyMode = true;
44203                 this.selectPrev();
44204             },
44205
44206             "down" : function(e){
44207                 if(!this.isExpanded()){
44208                     this.onTriggerClick();
44209                 }else{
44210                     this.inKeyMode = true;
44211                     this.selectNext();
44212                 }
44213             },
44214
44215             "enter" : function(e){
44216                 this.collapse();
44217                 
44218                 if(this.fireEvent("specialkey", this, e)){
44219                     this.onViewClick(false);
44220                 }
44221                 
44222                 return true;
44223             },
44224
44225             "esc" : function(e){
44226                 this.collapse();
44227             },
44228
44229             "tab" : function(e){
44230                 this.collapse();
44231                 
44232                 if(this.fireEvent("specialkey", this, e)){
44233                     this.onViewClick(false);
44234                 }
44235                 
44236                 return true;
44237             },
44238
44239             scope : this,
44240
44241             doRelay : function(foo, bar, hname){
44242                 if(hname == 'down' || this.scope.isExpanded()){
44243                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44244                 }
44245                 return true;
44246             },
44247
44248             forceKeyDown: true
44249         });
44250         
44251         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44252         
44253     },
44254     
44255     initNumberEvent : function(e)
44256     {
44257         this.inputEl().on("keydown" , this.fireKey,  this);
44258         this.inputEl().on("focus", this.onFocus,  this);
44259         this.inputEl().on("blur", this.onBlur,  this);
44260         
44261         this.inputEl().relayEvent('keyup', this);
44262         
44263         if(this.indicator){
44264             this.indicator.addClass('invisible');
44265         }
44266  
44267         this.originalValue = this.getValue();
44268         
44269         if(this.validationEvent == 'keyup'){
44270             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44271             this.inputEl().on('keyup', this.filterValidation, this);
44272         }
44273         else if(this.validationEvent !== false){
44274             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44275         }
44276         
44277         if(this.selectOnFocus){
44278             this.on("focus", this.preFocus, this);
44279             
44280         }
44281         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44282             this.inputEl().on("keypress", this.filterKeys, this);
44283         } else {
44284             this.inputEl().relayEvent('keypress', this);
44285         }
44286         
44287         var allowed = "0123456789";
44288         
44289         if(this.allowDecimals){
44290             allowed += this.decimalSeparator;
44291         }
44292         
44293         if(this.allowNegative){
44294             allowed += "-";
44295         }
44296         
44297         if(this.thousandsDelimiter) {
44298             allowed += ",";
44299         }
44300         
44301         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44302         
44303         var keyPress = function(e){
44304             
44305             var k = e.getKey();
44306             
44307             var c = e.getCharCode();
44308             
44309             if(
44310                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44311                     allowed.indexOf(String.fromCharCode(c)) === -1
44312             ){
44313                 e.stopEvent();
44314                 return;
44315             }
44316             
44317             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44318                 return;
44319             }
44320             
44321             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44322                 e.stopEvent();
44323             }
44324         };
44325         
44326         this.inputEl().on("keypress", keyPress, this);
44327         
44328     },
44329     
44330     onTriggerClick : function(e)
44331     {   
44332         if(this.disabled){
44333             return;
44334         }
44335         
44336         this.page = 0;
44337         this.loadNext = false;
44338         
44339         if(this.isExpanded()){
44340             this.collapse();
44341             return;
44342         }
44343         
44344         this.hasFocus = true;
44345         
44346         if(this.triggerAction == 'all') {
44347             this.doQuery(this.allQuery, true);
44348             return;
44349         }
44350         
44351         this.doQuery(this.getRawValue());
44352     },
44353     
44354     getCurrency : function()
44355     {   
44356         var v = this.currencyEl().getValue();
44357         
44358         return v;
44359     },
44360     
44361     restrictHeight : function()
44362     {
44363         this.list.alignTo(this.currencyEl(), this.listAlign);
44364         this.list.alignTo(this.currencyEl(), this.listAlign);
44365     },
44366     
44367     onViewClick : function(view, doFocus, el, e)
44368     {
44369         var index = this.view.getSelectedIndexes()[0];
44370         
44371         var r = this.store.getAt(index);
44372         
44373         if(r){
44374             this.onSelect(r, index);
44375         }
44376     },
44377     
44378     onSelect : function(record, index){
44379         
44380         if(this.fireEvent('beforeselect', this, record, index) !== false){
44381         
44382             this.setFromCurrencyData(index > -1 ? record.data : false);
44383             
44384             this.collapse();
44385             
44386             this.fireEvent('select', this, record, index);
44387         }
44388     },
44389     
44390     setFromCurrencyData : function(o)
44391     {
44392         var currency = '';
44393         
44394         this.lastCurrency = o;
44395         
44396         if (this.currencyField) {
44397             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44398         } else {
44399             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44400         }
44401         
44402         this.lastSelectionText = currency;
44403         
44404         //setting default currency
44405         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44406             this.setCurrency(this.defaultCurrency);
44407             return;
44408         }
44409         
44410         this.setCurrency(currency);
44411     },
44412     
44413     setFromData : function(o)
44414     {
44415         var c = {};
44416         
44417         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44418         
44419         this.setFromCurrencyData(c);
44420         
44421         var value = '';
44422         
44423         if (this.name) {
44424             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44425         } else {
44426             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44427         }
44428         
44429         this.setValue(value);
44430         
44431     },
44432     
44433     setCurrency : function(v)
44434     {   
44435         this.currencyValue = v;
44436         
44437         if(this.rendered){
44438             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44439             this.validate();
44440         }
44441     },
44442     
44443     setValue : function(v)
44444     {
44445         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44446         
44447         this.value = v;
44448         
44449         if(this.rendered){
44450             
44451             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44452             
44453             this.inputEl().dom.value = (v == '') ? '' :
44454                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44455             
44456             if(!this.allowZero && v === '0') {
44457                 this.hiddenEl().dom.value = '';
44458                 this.inputEl().dom.value = '';
44459             }
44460             
44461             this.validate();
44462         }
44463     },
44464     
44465     getRawValue : function()
44466     {
44467         var v = this.inputEl().getValue();
44468         
44469         return v;
44470     },
44471     
44472     getValue : function()
44473     {
44474         return this.fixPrecision(this.parseValue(this.getRawValue()));
44475     },
44476     
44477     parseValue : function(value)
44478     {
44479         if(this.thousandsDelimiter) {
44480             value += "";
44481             r = new RegExp(",", "g");
44482             value = value.replace(r, "");
44483         }
44484         
44485         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44486         return isNaN(value) ? '' : value;
44487         
44488     },
44489     
44490     fixPrecision : function(value)
44491     {
44492         if(this.thousandsDelimiter) {
44493             value += "";
44494             r = new RegExp(",", "g");
44495             value = value.replace(r, "");
44496         }
44497         
44498         var nan = isNaN(value);
44499         
44500         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44501             return nan ? '' : value;
44502         }
44503         return parseFloat(value).toFixed(this.decimalPrecision);
44504     },
44505     
44506     decimalPrecisionFcn : function(v)
44507     {
44508         return Math.floor(v);
44509     },
44510     
44511     validateValue : function(value)
44512     {
44513         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44514             return false;
44515         }
44516         
44517         var num = this.parseValue(value);
44518         
44519         if(isNaN(num)){
44520             this.markInvalid(String.format(this.nanText, value));
44521             return false;
44522         }
44523         
44524         if(num < this.minValue){
44525             this.markInvalid(String.format(this.minText, this.minValue));
44526             return false;
44527         }
44528         
44529         if(num > this.maxValue){
44530             this.markInvalid(String.format(this.maxText, this.maxValue));
44531             return false;
44532         }
44533         
44534         return true;
44535     },
44536     
44537     validate : function()
44538     {
44539         if(this.disabled || this.allowBlank){
44540             this.markValid();
44541             return true;
44542         }
44543         
44544         var currency = this.getCurrency();
44545         
44546         if(this.validateValue(this.getRawValue()) && currency.length){
44547             this.markValid();
44548             return true;
44549         }
44550         
44551         this.markInvalid();
44552         return false;
44553     },
44554     
44555     getName: function()
44556     {
44557         return this.name;
44558     },
44559     
44560     beforeBlur : function()
44561     {
44562         if(!this.castInt){
44563             return;
44564         }
44565         
44566         var v = this.parseValue(this.getRawValue());
44567         
44568         if(v || v == 0){
44569             this.setValue(v);
44570         }
44571     },
44572     
44573     onBlur : function()
44574     {
44575         this.beforeBlur();
44576         
44577         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44578             //this.el.removeClass(this.focusClass);
44579         }
44580         
44581         this.hasFocus = false;
44582         
44583         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44584             this.validate();
44585         }
44586         
44587         var v = this.getValue();
44588         
44589         if(String(v) !== String(this.startValue)){
44590             this.fireEvent('change', this, v, this.startValue);
44591         }
44592         
44593         this.fireEvent("blur", this);
44594     },
44595     
44596     inputEl : function()
44597     {
44598         return this.el.select('.roo-money-amount-input', true).first();
44599     },
44600     
44601     currencyEl : function()
44602     {
44603         return this.el.select('.roo-money-currency-input', true).first();
44604     },
44605     
44606     hiddenEl : function()
44607     {
44608         return this.el.select('input.hidden-number-input',true).first();
44609     }
44610     
44611 });/**
44612  * @class Roo.bootstrap.BezierSignature
44613  * @extends Roo.bootstrap.Component
44614  * Bootstrap BezierSignature class
44615  * This script refer to:
44616  *    Title: Signature Pad
44617  *    Author: szimek
44618  *    Availability: https://github.com/szimek/signature_pad
44619  *
44620  * @constructor
44621  * Create a new BezierSignature
44622  * @param {Object} config The config object
44623  */
44624
44625 Roo.bootstrap.BezierSignature = function(config){
44626     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44627     this.addEvents({
44628         "resize" : true
44629     });
44630 };
44631
44632 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44633 {
44634      
44635     curve_data: [],
44636     
44637     is_empty: true,
44638     
44639     mouse_btn_down: true,
44640     
44641     /**
44642      * @cfg {int} canvas height
44643      */
44644     canvas_height: '200px',
44645     
44646     /**
44647      * @cfg {float|function} Radius of a single dot.
44648      */ 
44649     dot_size: false,
44650     
44651     /**
44652      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44653      */
44654     min_width: 0.5,
44655     
44656     /**
44657      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44658      */
44659     max_width: 2.5,
44660     
44661     /**
44662      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44663      */
44664     throttle: 16,
44665     
44666     /**
44667      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44668      */
44669     min_distance: 5,
44670     
44671     /**
44672      * @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.
44673      */
44674     bg_color: 'rgba(0, 0, 0, 0)',
44675     
44676     /**
44677      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44678      */
44679     dot_color: 'black',
44680     
44681     /**
44682      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44683      */ 
44684     velocity_filter_weight: 0.7,
44685     
44686     /**
44687      * @cfg {function} Callback when stroke begin. 
44688      */
44689     onBegin: false,
44690     
44691     /**
44692      * @cfg {function} Callback when stroke end.
44693      */
44694     onEnd: false,
44695     
44696     getAutoCreate : function()
44697     {
44698         var cls = 'roo-signature column';
44699         
44700         if(this.cls){
44701             cls += ' ' + this.cls;
44702         }
44703         
44704         var col_sizes = [
44705             'lg',
44706             'md',
44707             'sm',
44708             'xs'
44709         ];
44710         
44711         for(var i = 0; i < col_sizes.length; i++) {
44712             if(this[col_sizes[i]]) {
44713                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44714             }
44715         }
44716         
44717         var cfg = {
44718             tag: 'div',
44719             cls: cls,
44720             cn: [
44721                 {
44722                     tag: 'div',
44723                     cls: 'roo-signature-body',
44724                     cn: [
44725                         {
44726                             tag: 'canvas',
44727                             cls: 'roo-signature-body-canvas',
44728                             height: this.canvas_height,
44729                             width: this.canvas_width
44730                         }
44731                     ]
44732                 },
44733                 {
44734                     tag: 'input',
44735                     type: 'file',
44736                     style: 'display: none'
44737                 }
44738             ]
44739         };
44740         
44741         return cfg;
44742     },
44743     
44744     initEvents: function() 
44745     {
44746         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44747         
44748         var canvas = this.canvasEl();
44749         
44750         // mouse && touch event swapping...
44751         canvas.dom.style.touchAction = 'none';
44752         canvas.dom.style.msTouchAction = 'none';
44753         
44754         this.mouse_btn_down = false;
44755         canvas.on('mousedown', this._handleMouseDown, this);
44756         canvas.on('mousemove', this._handleMouseMove, this);
44757         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44758         
44759         if (window.PointerEvent) {
44760             canvas.on('pointerdown', this._handleMouseDown, this);
44761             canvas.on('pointermove', this._handleMouseMove, this);
44762             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44763         }
44764         
44765         if ('ontouchstart' in window) {
44766             canvas.on('touchstart', this._handleTouchStart, this);
44767             canvas.on('touchmove', this._handleTouchMove, this);
44768             canvas.on('touchend', this._handleTouchEnd, this);
44769         }
44770         
44771         Roo.EventManager.onWindowResize(this.resize, this, true);
44772         
44773         // file input event
44774         this.fileEl().on('change', this.uploadImage, this);
44775         
44776         this.clear();
44777         
44778         this.resize();
44779     },
44780     
44781     resize: function(){
44782         
44783         var canvas = this.canvasEl().dom;
44784         var ctx = this.canvasElCtx();
44785         var img_data = false;
44786         
44787         if(canvas.width > 0) {
44788             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44789         }
44790         // setting canvas width will clean img data
44791         canvas.width = 0;
44792         
44793         var style = window.getComputedStyle ? 
44794             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44795             
44796         var padding_left = parseInt(style.paddingLeft) || 0;
44797         var padding_right = parseInt(style.paddingRight) || 0;
44798         
44799         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44800         
44801         if(img_data) {
44802             ctx.putImageData(img_data, 0, 0);
44803         }
44804     },
44805     
44806     _handleMouseDown: function(e)
44807     {
44808         if (e.browserEvent.which === 1) {
44809             this.mouse_btn_down = true;
44810             this.strokeBegin(e);
44811         }
44812     },
44813     
44814     _handleMouseMove: function (e)
44815     {
44816         if (this.mouse_btn_down) {
44817             this.strokeMoveUpdate(e);
44818         }
44819     },
44820     
44821     _handleMouseUp: function (e)
44822     {
44823         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44824             this.mouse_btn_down = false;
44825             this.strokeEnd(e);
44826         }
44827     },
44828     
44829     _handleTouchStart: function (e) {
44830         
44831         e.preventDefault();
44832         if (e.browserEvent.targetTouches.length === 1) {
44833             // var touch = e.browserEvent.changedTouches[0];
44834             // this.strokeBegin(touch);
44835             
44836              this.strokeBegin(e); // assume e catching the correct xy...
44837         }
44838     },
44839     
44840     _handleTouchMove: function (e) {
44841         e.preventDefault();
44842         // var touch = event.targetTouches[0];
44843         // _this._strokeMoveUpdate(touch);
44844         this.strokeMoveUpdate(e);
44845     },
44846     
44847     _handleTouchEnd: function (e) {
44848         var wasCanvasTouched = e.target === this.canvasEl().dom;
44849         if (wasCanvasTouched) {
44850             e.preventDefault();
44851             // var touch = event.changedTouches[0];
44852             // _this._strokeEnd(touch);
44853             this.strokeEnd(e);
44854         }
44855     },
44856     
44857     reset: function () {
44858         this._lastPoints = [];
44859         this._lastVelocity = 0;
44860         this._lastWidth = (this.min_width + this.max_width) / 2;
44861         this.canvasElCtx().fillStyle = this.dot_color;
44862     },
44863     
44864     strokeMoveUpdate: function(e)
44865     {
44866         this.strokeUpdate(e);
44867         
44868         if (this.throttle) {
44869             this.throttleStroke(this.strokeUpdate, this.throttle);
44870         }
44871         else {
44872             this.strokeUpdate(e);
44873         }
44874     },
44875     
44876     strokeBegin: function(e)
44877     {
44878         var newPointGroup = {
44879             color: this.dot_color,
44880             points: []
44881         };
44882         
44883         if (typeof this.onBegin === 'function') {
44884             this.onBegin(e);
44885         }
44886         
44887         this.curve_data.push(newPointGroup);
44888         this.reset();
44889         this.strokeUpdate(e);
44890     },
44891     
44892     strokeUpdate: function(e)
44893     {
44894         var rect = this.canvasEl().dom.getBoundingClientRect();
44895         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44896         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44897         var lastPoints = lastPointGroup.points;
44898         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44899         var isLastPointTooClose = lastPoint
44900             ? point.distanceTo(lastPoint) <= this.min_distance
44901             : false;
44902         var color = lastPointGroup.color;
44903         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44904             var curve = this.addPoint(point);
44905             if (!lastPoint) {
44906                 this.drawDot({color: color, point: point});
44907             }
44908             else if (curve) {
44909                 this.drawCurve({color: color, curve: curve});
44910             }
44911             lastPoints.push({
44912                 time: point.time,
44913                 x: point.x,
44914                 y: point.y
44915             });
44916         }
44917     },
44918     
44919     strokeEnd: function(e)
44920     {
44921         this.strokeUpdate(e);
44922         if (typeof this.onEnd === 'function') {
44923             this.onEnd(e);
44924         }
44925     },
44926     
44927     addPoint:  function (point) {
44928         var _lastPoints = this._lastPoints;
44929         _lastPoints.push(point);
44930         if (_lastPoints.length > 2) {
44931             if (_lastPoints.length === 3) {
44932                 _lastPoints.unshift(_lastPoints[0]);
44933             }
44934             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44935             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44936             _lastPoints.shift();
44937             return curve;
44938         }
44939         return null;
44940     },
44941     
44942     calculateCurveWidths: function (startPoint, endPoint) {
44943         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44944             (1 - this.velocity_filter_weight) * this._lastVelocity;
44945
44946         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44947         var widths = {
44948             end: newWidth,
44949             start: this._lastWidth
44950         };
44951         
44952         this._lastVelocity = velocity;
44953         this._lastWidth = newWidth;
44954         return widths;
44955     },
44956     
44957     drawDot: function (_a) {
44958         var color = _a.color, point = _a.point;
44959         var ctx = this.canvasElCtx();
44960         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44961         ctx.beginPath();
44962         this.drawCurveSegment(point.x, point.y, width);
44963         ctx.closePath();
44964         ctx.fillStyle = color;
44965         ctx.fill();
44966     },
44967     
44968     drawCurve: function (_a) {
44969         var color = _a.color, curve = _a.curve;
44970         var ctx = this.canvasElCtx();
44971         var widthDelta = curve.endWidth - curve.startWidth;
44972         var drawSteps = Math.floor(curve.length()) * 2;
44973         ctx.beginPath();
44974         ctx.fillStyle = color;
44975         for (var i = 0; i < drawSteps; i += 1) {
44976         var t = i / drawSteps;
44977         var tt = t * t;
44978         var ttt = tt * t;
44979         var u = 1 - t;
44980         var uu = u * u;
44981         var uuu = uu * u;
44982         var x = uuu * curve.startPoint.x;
44983         x += 3 * uu * t * curve.control1.x;
44984         x += 3 * u * tt * curve.control2.x;
44985         x += ttt * curve.endPoint.x;
44986         var y = uuu * curve.startPoint.y;
44987         y += 3 * uu * t * curve.control1.y;
44988         y += 3 * u * tt * curve.control2.y;
44989         y += ttt * curve.endPoint.y;
44990         var width = curve.startWidth + ttt * widthDelta;
44991         this.drawCurveSegment(x, y, width);
44992         }
44993         ctx.closePath();
44994         ctx.fill();
44995     },
44996     
44997     drawCurveSegment: function (x, y, width) {
44998         var ctx = this.canvasElCtx();
44999         ctx.moveTo(x, y);
45000         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45001         this.is_empty = false;
45002     },
45003     
45004     clear: function()
45005     {
45006         var ctx = this.canvasElCtx();
45007         var canvas = this.canvasEl().dom;
45008         ctx.fillStyle = this.bg_color;
45009         ctx.clearRect(0, 0, canvas.width, canvas.height);
45010         ctx.fillRect(0, 0, canvas.width, canvas.height);
45011         this.curve_data = [];
45012         this.reset();
45013         this.is_empty = true;
45014     },
45015     
45016     fileEl: function()
45017     {
45018         return  this.el.select('input',true).first();
45019     },
45020     
45021     canvasEl: function()
45022     {
45023         return this.el.select('canvas',true).first();
45024     },
45025     
45026     canvasElCtx: function()
45027     {
45028         return this.el.select('canvas',true).first().dom.getContext('2d');
45029     },
45030     
45031     getImage: function(type)
45032     {
45033         if(this.is_empty) {
45034             return false;
45035         }
45036         
45037         // encryption ?
45038         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45039     },
45040     
45041     drawFromImage: function(img_src)
45042     {
45043         var img = new Image();
45044         
45045         img.onload = function(){
45046             this.canvasElCtx().drawImage(img, 0, 0);
45047         }.bind(this);
45048         
45049         img.src = img_src;
45050         
45051         this.is_empty = false;
45052     },
45053     
45054     selectImage: function()
45055     {
45056         this.fileEl().dom.click();
45057     },
45058     
45059     uploadImage: function(e)
45060     {
45061         var reader = new FileReader();
45062         
45063         reader.onload = function(e){
45064             var img = new Image();
45065             img.onload = function(){
45066                 this.reset();
45067                 this.canvasElCtx().drawImage(img, 0, 0);
45068             }.bind(this);
45069             img.src = e.target.result;
45070         }.bind(this);
45071         
45072         reader.readAsDataURL(e.target.files[0]);
45073     },
45074     
45075     // Bezier Point Constructor
45076     Point: (function () {
45077         function Point(x, y, time) {
45078             this.x = x;
45079             this.y = y;
45080             this.time = time || Date.now();
45081         }
45082         Point.prototype.distanceTo = function (start) {
45083             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45084         };
45085         Point.prototype.equals = function (other) {
45086             return this.x === other.x && this.y === other.y && this.time === other.time;
45087         };
45088         Point.prototype.velocityFrom = function (start) {
45089             return this.time !== start.time
45090             ? this.distanceTo(start) / (this.time - start.time)
45091             : 0;
45092         };
45093         return Point;
45094     }()),
45095     
45096     
45097     // Bezier Constructor
45098     Bezier: (function () {
45099         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45100             this.startPoint = startPoint;
45101             this.control2 = control2;
45102             this.control1 = control1;
45103             this.endPoint = endPoint;
45104             this.startWidth = startWidth;
45105             this.endWidth = endWidth;
45106         }
45107         Bezier.fromPoints = function (points, widths, scope) {
45108             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45109             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45110             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45111         };
45112         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45113             var dx1 = s1.x - s2.x;
45114             var dy1 = s1.y - s2.y;
45115             var dx2 = s2.x - s3.x;
45116             var dy2 = s2.y - s3.y;
45117             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45118             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45119             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45120             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45121             var dxm = m1.x - m2.x;
45122             var dym = m1.y - m2.y;
45123             var k = l2 / (l1 + l2);
45124             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45125             var tx = s2.x - cm.x;
45126             var ty = s2.y - cm.y;
45127             return {
45128                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45129                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45130             };
45131         };
45132         Bezier.prototype.length = function () {
45133             var steps = 10;
45134             var length = 0;
45135             var px;
45136             var py;
45137             for (var i = 0; i <= steps; i += 1) {
45138                 var t = i / steps;
45139                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45140                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45141                 if (i > 0) {
45142                     var xdiff = cx - px;
45143                     var ydiff = cy - py;
45144                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45145                 }
45146                 px = cx;
45147                 py = cy;
45148             }
45149             return length;
45150         };
45151         Bezier.prototype.point = function (t, start, c1, c2, end) {
45152             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45153             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45154             + (3.0 * c2 * (1.0 - t) * t * t)
45155             + (end * t * t * t);
45156         };
45157         return Bezier;
45158     }()),
45159     
45160     throttleStroke: function(fn, wait) {
45161       if (wait === void 0) { wait = 250; }
45162       var previous = 0;
45163       var timeout = null;
45164       var result;
45165       var storedContext;
45166       var storedArgs;
45167       var later = function () {
45168           previous = Date.now();
45169           timeout = null;
45170           result = fn.apply(storedContext, storedArgs);
45171           if (!timeout) {
45172               storedContext = null;
45173               storedArgs = [];
45174           }
45175       };
45176       return function wrapper() {
45177           var args = [];
45178           for (var _i = 0; _i < arguments.length; _i++) {
45179               args[_i] = arguments[_i];
45180           }
45181           var now = Date.now();
45182           var remaining = wait - (now - previous);
45183           storedContext = this;
45184           storedArgs = args;
45185           if (remaining <= 0 || remaining > wait) {
45186               if (timeout) {
45187                   clearTimeout(timeout);
45188                   timeout = null;
45189               }
45190               previous = now;
45191               result = fn.apply(storedContext, storedArgs);
45192               if (!timeout) {
45193                   storedContext = null;
45194                   storedArgs = [];
45195               }
45196           }
45197           else if (!timeout) {
45198               timeout = window.setTimeout(later, remaining);
45199           }
45200           return result;
45201       };
45202   }
45203   
45204 });
45205
45206  
45207
45208  // old names for form elements
45209 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45210 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45211 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45212 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45213 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45214 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45215 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45216 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45217 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45218 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45219 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45220 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45221 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45222 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45223 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45224 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45225 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45226 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45227 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45228 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45229 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45230 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45231 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45232 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45233 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45234 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45235
45236 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45237 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45238
45239 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45240 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45241
45242 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45243 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45244 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45245 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45246